diff --git a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md
index a464b6ba..578f5ba7 100644
--- a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md
+++ b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotFooter.md
@@ -60,6 +60,16 @@ Attachments can also be added to the chatbot via [drag and drop.](/patternfly-ai
```
+### Message bar with stop button
+
+If you are using streaming, you can add a stop button to the message bar that allows users to stop a response from a chatbot.
+
+To enable the stop button, set `hasStopButton` to `true` and pass in a `handleStopButton` callback function. You can use this callback to trigger an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) configured as part of your API call.
+
+```js file="./ChatbotMessageBarStop.tsx"
+
+```
+
### Footer with message bar and footnote
A simple footer with a message bar and footnote would have this code structure:
diff --git a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotMessageBarStop.tsx b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotMessageBarStop.tsx
new file mode 100644
index 00000000..f94f2d8b
--- /dev/null
+++ b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/ChatbotFooter/ChatbotMessageBarStop.tsx
@@ -0,0 +1,10 @@
+import React from 'react';
+import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar';
+
+export const ChatbotMessageBarStop: React.FunctionComponent = () => {
+ const handleSend = (message) => alert(message);
+
+ const handleStopButton = () => alert('Stop button clicked');
+
+ return ;
+};
diff --git a/packages/module/src/MessageBar/AttachButton.tsx b/packages/module/src/MessageBar/AttachButton.tsx
index 022c7b00..51d8f69b 100644
--- a/packages/module/src/MessageBar/AttachButton.tsx
+++ b/packages/module/src/MessageBar/AttachButton.tsx
@@ -9,18 +9,20 @@ import { useDropzone } from 'react-dropzone';
import { PaperclipIcon } from '@patternfly/react-icons/dist/esm/icons/paperclip-icon';
export interface AttachButtonProps extends ButtonProps {
- /** OnClick Handler for the Attach Button */
- onClick?: ((event: MouseEvent | React.MouseEvent | KeyboardEvent) => void) | undefined;
- /** Callback function for attach button when an attachment is made */
+ /** Callback for when button is clicked */
+ onClick?: (event: React.MouseEvent) => void;
+ /** Callback function for AttachButton when an attachment is made */
onAttachAccepted?: (data: File[], event: DropEvent) => void;
- /** Class Name for the Attach button */
+ /** Class name for AttachButton */
className?: string;
- /** Props to control is the attach button should be disabled */
+ /** Props to control if the AttachButton should be disabled */
isDisabled?: boolean;
/** Props to control the PF Tooltip component */
tooltipProps?: TooltipProps;
/** Ref applied to AttachButton and used in tooltip */
innerRef?: React.Ref;
+ /** English text "Attach" used in the tooltip */
+ tooltipContent?: string;
}
const AttachButtonBase: React.FunctionComponent = ({
@@ -30,6 +32,7 @@ const AttachButtonBase: React.FunctionComponent = ({
className,
tooltipProps,
innerRef,
+ tooltipContent = 'Attach',
...props
}: AttachButtonProps) => {
const { open, getInputProps } = useDropzone({
@@ -43,7 +46,7 @@ const AttachButtonBase: React.FunctionComponent = ({
= ({
variant="plain"
ref={innerRef}
className={`pf-chatbot__button--attach ${className ?? ''}`}
- aria-label={props['aria-label'] || 'Attach Button'}
+ aria-label={props['aria-label'] || 'Attach button'}
isDisabled={isDisabled}
onClick={onClick ?? open}
icon={
diff --git a/packages/module/src/MessageBar/MessageBar.scss b/packages/module/src/MessageBar/MessageBar.scss
index 7640a746..87c80fa8 100644
--- a/packages/module/src/MessageBar/MessageBar.scss
+++ b/packages/module/src/MessageBar/MessageBar.scss
@@ -2,6 +2,7 @@
@import './AttachButton';
@import './MicrophoneButton';
@import './SendButton';
+@import './StopButton';
// ============================================================================
// Chatbot Footer - Message Bar
diff --git a/packages/module/src/MessageBar/MessageBar.tsx b/packages/module/src/MessageBar/MessageBar.tsx
index a9e1d7cf..fe69852c 100644
--- a/packages/module/src/MessageBar/MessageBar.tsx
+++ b/packages/module/src/MessageBar/MessageBar.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import { DropEvent, TextAreaProps } from '@patternfly/react-core';
+import { ButtonProps, DropEvent, TextAreaProps } from '@patternfly/react-core';
import { AutoTextArea } from 'react-textarea-auto-witdth-height';
// Import Chatbot components
@@ -7,6 +7,7 @@ import SendButton from './SendButton';
import MicrophoneButton from './MicrophoneButton';
import { AttachButton } from './AttachButton';
import AttachMenu from '../AttachMenu';
+import StopButton from './StopButton';
export interface MessageBarWithAttachMenuProps {
/** Flag to enable whether attach menu is open */
@@ -40,12 +41,23 @@ export interface MessageBarProps extends TextAreaProps {
hasAttachButton?: boolean;
/** Flag to enable the Microphone button */
hasMicrophoneButton?: boolean;
+ /** Flag to enable the Stop button, used for streaming content */
+ hasStopButton?: boolean;
+ /** Callback function for when stop button is clicked */
+ handleStopButton?: (event: React.MouseEvent) => void;
/** Callback function for when attach button is used to upload a file */
handleAttach?: (data: File[], event: DropEvent) => void;
/** Props to enable a menu that opens when the Attach button is clicked, instead of the attachment window */
attachMenuProps?: MessageBarWithAttachMenuProps;
/** Flag to provide manual control over whether send button is disabled */
isSendButtonDisabled?: boolean;
+ /** Prop to allow passage of additional props to buttons */
+ buttonProps?: {
+ attach: { tooltipContent?: string; props?: ButtonProps };
+ stop: { tooltipContent?: string; props?: ButtonProps };
+ send: { tooltipContent?: string; props?: ButtonProps };
+ microphone: { tooltipContent?: { active: string; inactive: string }; props?: ButtonProps };
+ };
}
export const MessageBar: React.FunctionComponent = ({
@@ -57,6 +69,9 @@ export const MessageBar: React.FunctionComponent = ({
handleAttach,
attachMenuProps,
isSendButtonDisabled,
+ handleStopButton,
+ hasStopButton,
+ buttonProps,
...props
}: MessageBarProps) => {
// Text Input
@@ -83,7 +98,7 @@ export const MessageBar: React.FunctionComponent = ({
(event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
- if (!isSendButtonDisabled) {
+ if (!isSendButtonDisabled && !hasStopButton) {
handleSend();
}
}
@@ -96,38 +111,72 @@ export const MessageBar: React.FunctionComponent = ({
attachMenuProps?.onAttachMenuToggleClick();
};
- const messageBarContents = (
- <>
-