{loadingWord}
diff --git a/packages/module/src/Message/QuickResponse/QuickResponse.test.tsx b/packages/module/src/Message/QuickResponse/QuickResponse.test.tsx
new file mode 100644
index 000000000..67bbf2076
--- /dev/null
+++ b/packages/module/src/Message/QuickResponse/QuickResponse.test.tsx
@@ -0,0 +1,131 @@
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import '@testing-library/jest-dom';
+import QuickResponse from './QuickResponse';
+
+test('Renders with quick responses', () => {
+ const quickResponses = [
+ { id: '1', content: 'Response 1' },
+ { id: '2', content: 'Response 2' },
+ { id: '3', content: 'Response 3' }
+ ];
+ render(
);
+
+ expect(screen.getByText('Response 1')).toBeVisible();
+ expect(screen.getByText('Response 2')).toBeVisible();
+ expect(screen.getByText('Response 3')).toBeVisible();
+});
+
+test('Renders with compact styling', () => {
+ const quickResponses = [{ id: '1', content: 'Compact response' }];
+ render(
);
+
+ expect(screen.getByText('Compact response').closest('.pf-v6-c-label')).toHaveClass('pf-m-compact');
+});
+
+test('Renders with custom className on response', () => {
+ const quickResponses = [{ id: '1', content: 'Custom class response', className: 'custom-response-class' }];
+ render(
);
+
+ expect(screen.getByText('Custom class response').closest('.pf-v6-c-label')).toHaveClass('custom-response-class');
+});
+
+test('Renders with custom container className', () => {
+ const quickResponses = [
+ { id: '1', content: 'Response 1' },
+ { id: '2', content: 'Response 2' }
+ ];
+ render(
+
+ );
+
+ expect(screen.getByText('Response 1').closest('.pf-v6-c-label-group')).toHaveClass('custom-container-class');
+});
+
+test('Spreads additional custom container props', () => {
+ const quickResponses = [
+ { id: '1', content: 'Response 1' },
+ { id: '2', content: 'Response 2' }
+ ];
+ render(
);
+
+ expect(screen.getByText('Response 1').closest('.pf-v6-c-label-group__list')).toHaveAttribute(
+ 'id',
+ 'custom-container-id'
+ );
+});
+
+test('Renders with pf-chatbot__message-quick-response--selected class after click', async () => {
+ const user = userEvent.setup();
+ const quickResponses = [
+ { id: '1', content: 'Response 1' },
+ { id: '2', content: 'Response 2' }
+ ];
+ render(
);
+
+ await user.click(screen.getByText('Response 1'));
+
+ expect(screen.getByText('Response 1').closest('.pf-v6-c-label')).toHaveClass(
+ 'pf-chatbot__message-quick-response--selected'
+ );
+});
+
+test('Does not calls onClick handler when not passed', async () => {
+ const user = userEvent.setup();
+ const handleClick = jest.fn();
+ const quickResponses = [{ id: '1', content: 'Clickable response' }];
+ render(
);
+
+ await user.click(screen.getByText('Clickable response'));
+
+ expect(handleClick).not.toHaveBeenCalled();
+});
+
+test('Calls onClick handler when passed', async () => {
+ const user = userEvent.setup();
+ const handleClick = jest.fn();
+ const quickResponses = [{ id: '1', content: 'Clickable response', onClick: handleClick }];
+ render(
);
+
+ await user.click(screen.getByText('Clickable response'));
+
+ expect(handleClick).toHaveBeenCalled();
+});
+
+test('Does not call onSelect when not passed', async () => {
+ const user = userEvent.setup();
+ const handleSelect = jest.fn();
+ const quickResponses = [
+ { id: '1', content: 'Response 1' },
+ { id: '2', content: 'Response 2' }
+ ];
+ render(
);
+
+ await user.click(screen.getByText('Response 2'));
+
+ expect(handleSelect).not.toHaveBeenCalled();
+});
+
+test('Calls onSelect when passed', async () => {
+ const user = userEvent.setup();
+ const handleSelect = jest.fn();
+ const quickResponses = [
+ { id: '1', content: 'Response 1' },
+ { id: '2', content: 'Response 2' }
+ ];
+ render(
);
+
+ await user.click(screen.getByText('Response 2'));
+
+ expect(handleSelect).toHaveBeenCalledWith('2');
+});
+
+test('Spreads additional response props', () => {
+ const quickResponses = [{ id: '1', content: 'Response with props', isCompact: true, 'aria-label': 'Test label' }];
+ render(
);
+
+ expect(screen.getByText('Response with props').closest('.pf-v6-c-label')).toHaveAttribute('aria-label', 'Test label');
+});
diff --git a/packages/module/src/Message/QuickResponse/QuickResponse.tsx b/packages/module/src/Message/QuickResponse/QuickResponse.tsx
index 36e62cdbd..033cdd9fa 100644
--- a/packages/module/src/Message/QuickResponse/QuickResponse.tsx
+++ b/packages/module/src/Message/QuickResponse/QuickResponse.tsx
@@ -2,6 +2,7 @@ import type { FunctionComponent } from 'react';
import { useState } from 'react';
import { Label, LabelGroup, LabelGroupProps, LabelProps } from '@patternfly/react-core';
import { CheckIcon } from '@patternfly/react-icons';
+import { css } from '@patternfly/react-styles';
export interface QuickResponse extends Omit
{
content: string;
@@ -35,7 +36,7 @@ export const QuickResponse: FunctionComponent = ({
};
return (
{quickResponses.map(({ id, onClick, content, className, ...props }: QuickResponse) => (
@@ -45,7 +46,7 @@ export const QuickResponse: FunctionComponent = ({
color="blue"
key={id}
onClick={() => handleQuickResponseClick(id, onClick)}
- className={`${id === selectedQuickResponse ? 'pf-chatbot__message-quick-response--selected' : ''} ${className ? className : ''}`}
+ className={css(id === selectedQuickResponse && 'pf-chatbot__message-quick-response--selected', className)}
isCompact={isCompact}
{...props}
>
diff --git a/packages/module/src/Message/QuickResponse/index.ts b/packages/module/src/Message/QuickResponse/index.ts
new file mode 100644
index 000000000..edcff972b
--- /dev/null
+++ b/packages/module/src/Message/QuickResponse/index.ts
@@ -0,0 +1 @@
+export * from './QuickResponse';
diff --git a/packages/module/src/Message/QuickStarts/QuickStartTile.tsx b/packages/module/src/Message/QuickStarts/QuickStartTile.tsx
index ae8528f5d..c154674e7 100644
--- a/packages/module/src/Message/QuickStarts/QuickStartTile.tsx
+++ b/packages/module/src/Message/QuickStarts/QuickStartTile.tsx
@@ -53,7 +53,7 @@ export interface QuickStartTileProps {
isCompact?: boolean;
}
-const QuickStartTile: FC = ({
+export const QuickStartTile: FC = ({
className,
quickStart,
onClick,
diff --git a/packages/module/src/Message/QuickStarts/index.ts b/packages/module/src/Message/QuickStarts/index.ts
new file mode 100644
index 000000000..a7688a023
--- /dev/null
+++ b/packages/module/src/Message/QuickStarts/index.ts
@@ -0,0 +1,2 @@
+export * from './QuickStartTile';
+export * from './types';
diff --git a/packages/module/src/Message/UserFeedback/UserFeedback.tsx b/packages/module/src/Message/UserFeedback/UserFeedback.tsx
index f71854b99..39a524161 100644
--- a/packages/module/src/Message/UserFeedback/UserFeedback.tsx
+++ b/packages/module/src/Message/UserFeedback/UserFeedback.tsx
@@ -78,7 +78,7 @@ export interface UserFeedbackProps extends Omit, OUIAProp
privacyStatement?: string;
}
-const UserFeedback: FunctionComponent = ({
+export const UserFeedback: FunctionComponent = ({
className,
timestamp,
title = 'Why did you choose this rating?',
diff --git a/packages/module/src/Message/UserFeedback/UserFeedbackComplete.tsx b/packages/module/src/Message/UserFeedback/UserFeedbackComplete.tsx
index 4fc50abc1..6b13e3751 100644
--- a/packages/module/src/Message/UserFeedback/UserFeedbackComplete.tsx
+++ b/packages/module/src/Message/UserFeedback/UserFeedbackComplete.tsx
@@ -2,10 +2,7 @@
// Chatbot Main - Messages - Feedback Complete Card
// ============================================================================
import type { MouseEvent as ReactMouseEvent, FunctionComponent } from 'react';
-
import { useState, useRef, useEffect } from 'react';
-
-// Import PatternFly components
import { Card, CardBody, CardHeader, CardProps, CardTitle, OUIAProps, useOUIAProps } from '@patternfly/react-core';
import CloseButton from './CloseButton';
@@ -48,7 +45,7 @@ export interface UserFeedbackCompleteProps extends Omit, OUIAP
timestamp?: string;
}
-const UserFeedbackComplete: FunctionComponent = ({
+export const UserFeedbackComplete: FunctionComponent = ({
className,
title = 'Feedback submitted',
body = "We've received your response. Thank you for sharing your feedback!",
diff --git a/packages/module/src/Message/UserFeedback/index.ts b/packages/module/src/Message/UserFeedback/index.ts
new file mode 100644
index 000000000..26df8cba9
--- /dev/null
+++ b/packages/module/src/Message/UserFeedback/index.ts
@@ -0,0 +1,2 @@
+export * from './UserFeedback';
+export * from './UserFeedbackComplete';
diff --git a/packages/module/src/Message/index.ts b/packages/module/src/Message/index.ts
index d62ce192d..606605d8a 100644
--- a/packages/module/src/Message/index.ts
+++ b/packages/module/src/Message/index.ts
@@ -1,4 +1,12 @@
export { default } from './Message';
export { rehypeCodeBlockToggle } from './Plugins/rehypeCodeBlockToggle';
+export * from './ErrorMessage/ErrorMessage';
+export * from './MessageAndActions';
+export * from './MessageAttachments';
export * from './Message';
+export * from './MessageLoading';
+export * from './MessageInput';
+export * from './QuickResponse';
+export * from './QuickStarts';
+export * from './UserFeedback';
diff --git a/packages/module/src/ResponseActions/ResponseActions.tsx b/packages/module/src/ResponseActions/ResponseActions.tsx
index 7598f9227..c1cfbcff8 100644
--- a/packages/module/src/ResponseActions/ResponseActions.tsx
+++ b/packages/module/src/ResponseActions/ResponseActions.tsx
@@ -42,6 +42,12 @@ export interface ActionProps extends Omit {
type ExtendedActionProps = ActionProps & {
[key: string]: any;
};
+
+/**
+ * The various actions that can be attached to a bot message for users to interact with.
+ * Use this component when passing children to Message to customize its structure.
+ */
+
export interface ResponseActionProps {
/** Props for message actions, such as feedback (positive or negative), copy button, share, and listen */
actions: Record & {
diff --git a/packages/module/src/ResponseActions/ResponseActionsGroups.test.tsx b/packages/module/src/ResponseActions/ResponseActionsGroups.test.tsx
new file mode 100644
index 000000000..be14f6c6e
--- /dev/null
+++ b/packages/module/src/ResponseActions/ResponseActionsGroups.test.tsx
@@ -0,0 +1,23 @@
+import '@testing-library/jest-dom';
+import { render, screen } from '@testing-library/react';
+import ResponseActionsGroups from './ResponseActionsGroups';
+
+test('Renders with children', () => {
+ render(Test content);
+ expect(screen.getByText('Test content')).toBeInTheDocument();
+});
+
+test('Renders with pf-chatbot__response-actions-groups class by default', () => {
+ render(Test content);
+ expect(screen.getByText('Test content')).toHaveClass('pf-chatbot__response-actions-groups', { exact: true });
+});
+
+test('Renders with custom className', () => {
+ render(Test content);
+ expect(screen.getByText('Test content')).toHaveClass('custom-class');
+});
+
+test('Spreads additional props', () => {
+ render(Test content);
+ expect(screen.getByText('Test content')).toHaveAttribute('id', 'test-id');
+});
diff --git a/packages/module/src/ResponseActions/ResponseActionsGroups.tsx b/packages/module/src/ResponseActions/ResponseActionsGroups.tsx
new file mode 100644
index 000000000..602654782
--- /dev/null
+++ b/packages/module/src/ResponseActions/ResponseActionsGroups.tsx
@@ -0,0 +1,28 @@
+// ============================================================================
+// Response Actions Groups - Container for multiple action groups
+// ============================================================================
+import { FunctionComponent, HTMLProps, ReactNode } from 'react';
+import { css } from '@patternfly/react-styles';
+
+/**
+ * The container for grouping multiple related ResponseActions components, typically used for having different persistence states amongst groups.
+ * Use this component when passing children to Message to customize its structure.
+ */
+export interface ResponseActionsGroupsProps extends HTMLProps {
+ /** Content to render inside the response actions groups container */
+ children: ReactNode;
+ /** Additional classes applied to the response actions groups container. */
+ className?: string;
+}
+
+export const ResponseActionsGroups: FunctionComponent = ({
+ children,
+ className,
+ ...props
+}) => (
+
+ {children}
+
+);
+
+export default ResponseActionsGroups;
diff --git a/packages/module/src/ResponseActions/index.ts b/packages/module/src/ResponseActions/index.ts
index c429555cb..08a5e9496 100644
--- a/packages/module/src/ResponseActions/index.ts
+++ b/packages/module/src/ResponseActions/index.ts
@@ -1,3 +1,4 @@
export { default } from './ResponseActions';
export * from './ResponseActions';
+export * from './ResponseActionsGroups';