From b54d4cb87dff38ce1c6b7d634acba302553f8e49 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Thu, 21 Aug 2025 14:17:42 -0400 Subject: [PATCH 1/6] feat(ToolResponse): Add component --- .../Messages/MessageWithToolResponse.tsx | 141 ++++++++++++++++++ .../chatbot/examples/Messages/Messages.md | 10 +- packages/module/src/Message/Message.test.tsx | 23 +++ packages/module/src/Message/Message.tsx | 5 + .../module/src/ToolResponse/ToolResponse.scss | 34 +++++ .../src/ToolResponse/ToolResponse.test.tsx | 101 +++++++++++++ .../module/src/ToolResponse/ToolResponse.tsx | 98 ++++++++++++ packages/module/src/ToolResponse/index.ts | 3 + packages/module/src/index.ts | 3 + packages/module/src/main.scss | 1 + 10 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx create mode 100644 packages/module/src/ToolResponse/ToolResponse.scss create mode 100644 packages/module/src/ToolResponse/ToolResponse.test.tsx create mode 100644 packages/module/src/ToolResponse/ToolResponse.tsx create mode 100644 packages/module/src/ToolResponse/index.ts diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx new file mode 100644 index 000000000..b78ac541c --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx @@ -0,0 +1,141 @@ +import { useState, FunctionComponent, MouseEvent as ReactMouseEvent } from 'react'; +import Message from '@patternfly/chatbot/dist/dynamic/Message'; +import patternflyAvatar from './patternfly_avatar.jpg'; +import { CopyIcon, WrenchIcon } from '@patternfly/react-icons'; +import { + Button, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, + ExpandableSection, + ExpandableSectionVariant, + Flex, + FlexItem, + Label +} from '@patternfly/react-core'; + +export const MessageWithToolResponseExample: FunctionComponent = () => { + const [isExpanded, setIsExpanded] = useState(false); + + const onToggle = (_event: ReactMouseEvent, isExpanded: boolean) => { + setIsExpanded(isExpanded); + }; + + return ( + + + + + + + + + Tool name + + + + + Execution time: + 0.12s + + + + + + + + + ), + cardBody: ( + <> + + + Parameters + + + Optional description text goes here + + + + + + + + + + + + + + + + + + + + + Response + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque nec dignissim turpis, et tristique + purus. Phasellus efficitur ante quis dolor viverra imperdiet. Orci varius natoque penatibus et + magnis dis parturient montes, nascetur ridiculus mus. Pellentesque laoreet, sem ac elementum semper, + lectus mauris vestibulum nulla, eget volutpat massa neque vel turpis. Donec finibus enim eu leo + accumsan consectetur. Praesent massa diam, tincidunt eu dui ac, ullamcorper elementum est. Phasellus + metus felis, venenatis vitae semper nec, porta a metus. Vestibulum justo nisi, imperdiet id eleifend + at, varius nec lorem. Fusce porttitor mollis nibh, ut elementum ante commodo tincidunt. Integer + tincidunt at ipsum non aliquet. + + + + + + ) + }} + /> + ); +}; diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md index 3b9139095..caef87e76 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md @@ -34,7 +34,7 @@ import Message from '@patternfly/chatbot/dist/dynamic/Message'; import MessageDivider from '@patternfly/chatbot/dist/dynamic/MessageDivider'; import { rehypeCodeBlockToggle } from '@patternfly/chatbot/dist/esm/Message/Plugins/rehypeCodeBlockToggle'; import SourcesCard from '@patternfly/chatbot/dist/dynamic/SourcesCard'; -import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, OutlinedQuestionCircleIcon, RedoIcon, RobotIcon } from '@patternfly/react-icons'; +import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CopyIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, OutlinedQuestionCircleIcon, RedoIcon, RobotIcon, WrenchIcon } from '@patternfly/react-icons'; import patternflyAvatar from './patternfly_avatar.jpg'; import AttachmentEdit from '@patternfly/chatbot/dist/dynamic/AttachmentEdit'; import FileDetails from '@patternfly/chatbot/dist/dynamic/FileDetails'; @@ -178,6 +178,14 @@ The API for a source requires a link at minimum, but we strongly recommend provi ``` +### Messages with tool responses + +If you are using [model context protocol (MCP)](https://www.redhat.com/en/blog/model-context-protocol-discover-missing-link-ai-integration), you may find it useful to display information on tool responses as part of a message. Passing `toolResponse` to `` allows you to display a card with an optional subheading and body, as well as custom card content. Content is intentionally left fully customizable for now as this is an evolving area. + +```js file="./MessageWithToolResponse.tsx" + +``` + ### Messages with quick start tiles [Quick start](/extensions/quick-starts/) tiles can be added to messages via the `quickStarts` prop. Users can initiate the quick start from a link within the message tile. diff --git a/packages/module/src/Message/Message.test.tsx b/packages/module/src/Message/Message.test.tsx index e4bc49833..0227aaae4 100644 --- a/packages/module/src/Message/Message.test.tsx +++ b/packages/module/src/Message/Message.test.tsx @@ -675,6 +675,29 @@ describe('Message', () => { ); expect(screen.getAllByRole('img')[1]).toHaveAttribute('src', 'test.png'); }); + it('should handle tool response correctly', async () => { + render( + + ); + expect(screen.getByRole('button', { name: /Tool response: Name/i })).toBeTruthy(); + expect(screen.getByText('Thought for 3 seconds')).toBeTruthy(); + expect(screen.getByText('Lorem ipsum dolor sit amet')).toBeTruthy(); + expect(screen.getByText('Card title')).toBeTruthy(); + expect(screen.getByText('Card body')).toBeTruthy(); + }); it('should handle block quote correctly', () => { render(); expect(screen.getByText(/Blockquotes can also be nested.../)).toBeTruthy(); diff --git a/packages/module/src/Message/Message.tsx b/packages/module/src/Message/Message.tsx index bdc547d68..9e28d4f1e 100644 --- a/packages/module/src/Message/Message.tsx +++ b/packages/module/src/Message/Message.tsx @@ -49,6 +49,7 @@ import LinkMessage from './LinkMessage/LinkMessage'; import ErrorMessage from './ErrorMessage/ErrorMessage'; import MessageInput from './MessageInput'; import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs'; +import ToolResponse, { ToolResponseProps } from '../ToolResponse'; export interface MessageAttachment { /** Name of file attached to the message */ @@ -189,6 +190,8 @@ export interface MessageProps extends Omit, 'role'> { isMarkdownDisabled?: boolean; /** 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 */ reactMarkdownProps?: Options; + /** Props for tool response card */ + toolResponse?: ToolResponseProps; } export const MessageBase: FunctionComponent = ({ @@ -230,6 +233,7 @@ export const MessageBase: FunctionComponent = ({ isCompact, isMarkdownDisabled, reactMarkdownProps, + toolResponse, ...props }: MessageProps) => { const [messageText, setMessageText] = useState(content); @@ -376,6 +380,7 @@ export const MessageBase: FunctionComponent = ({
{renderMessage()} {afterMainContent && <>{afterMainContent}} + {toolResponse && } {!isLoading && sources && } {quickStarts && quickStarts.quickStart && ( { + const defaultProps = { + collapsedToggleText: 'Show details', + expandedToggleText: 'Hide details', + cardTitle: 'Title', + cardBody: 'Body' + }; + + it('should render with required props only', () => { + render(); + expect(screen.getByText('Title')).toBeTruthy(); + expect(screen.getByText('Body')).toBeTruthy(); + expect(screen.getByText('Hide details')).toBeTruthy(); + }); + + it('should render expanded by default', () => { + render(); + expect(screen.getByText('Hide details')).toBeTruthy(); + expect(screen.queryByText('Show details')).toBeFalsy(); + }); + + it('should toggle between expanded and collapsed states', async () => { + const user = userEvent.setup(); + render(); + const toggleButton = screen.getByRole('button', { name: /Hide details/i }); + expect(screen.getByText('Hide details')).toBeTruthy(); + await user.click(toggleButton); + expect(screen.getByText('Show details')).toBeTruthy(); + expect(screen.queryByText('Hide details')).toBeFalsy(); + + // Click to expand again + await user.click(screen.getByRole('button', { name: /Show details/i })); + expect(screen.getByText('Hide details')).toBeTruthy(); + expect(screen.queryByText('Show details')).toBeFalsy(); + }); + + it('should render subheading when provided', () => { + const subheading = 'Tool execution result'; + render(); + expect(screen.getByText(subheading)).toBeTruthy(); + }); + + it('should render body content when provided', () => { + const body = 'This is the tool response body content'; + render(); + expect(screen.getByText(body)).toBeTruthy(); + }); + + it('should render with complex content including React elements', () => { + const body = ( +
+

Complex body content

+
    +
  • Item 1
  • +
  • Item 2
  • +
+
+ ); + const cardTitle = API Response; + const cardBody = ( +
+ {"{ status: 'success' }"} +
+ ); + + render(); + expect(screen.getByText('Complex body content')).toBeTruthy(); + expect(screen.getByText('Item 1')).toBeTruthy(); + expect(screen.getByText('Item 2')).toBeTruthy(); + expect(screen.getByText('API Response')).toBeTruthy(); + expect(screen.getByText("{ status: 'success' }")).toBeTruthy(); + }); + + it('should apply custom className from cardProps', () => { + const { container } = render( + + ); + expect(container.querySelector('.custom-tool-response-class')).toBeTruthy(); + }); + + it('should pass through expandableSectionProps', () => { + render(); + expect(document.querySelector('.custom-expandable-class')).toBeTruthy(); + }); + + it('should pass through toolResponseCardProps', () => { + render(); + expect(document.querySelector('.custom-card-class')).toBeTruthy(); + }); + + it('should not render subheading span when subheading is not provided', () => { + const { container } = render(); + const subheadingContainer = container.querySelector('.pf-chatbot__tool-response-subheading'); + expect(subheadingContainer).toBeFalsy(); + }); +}); diff --git a/packages/module/src/ToolResponse/ToolResponse.tsx b/packages/module/src/ToolResponse/ToolResponse.tsx new file mode 100644 index 000000000..06ced47ea --- /dev/null +++ b/packages/module/src/ToolResponse/ToolResponse.tsx @@ -0,0 +1,98 @@ +// ============================================================================ +// Tool Response Card +// ============================================================================ +import { + Card, + CardBody, + CardBodyProps, + CardProps, + CardTitle, + CardTitleProps, + Divider, + DividerProps, + ExpandableSection, + ExpandableSectionProps +} from '@patternfly/react-core'; +import { useState, type FunctionComponent } from 'react'; + +export interface ToolResponseProps { + /** Toggle text shown on expandable section when it is collapsed */ + collapsedToggleText: string; + /** Toggle text shown on expandable section when it is expanded */ + expandedToggleText: string; + /** Additional props passed to expandable section */ + expandableSectionProps?: Omit; + /** Subheading rendered inside expandable section */ + subheading?: string; + /** Body text rendered inside expandable section */ + body?: React.ReactNode | string; + /** Content passed into tool response card body */ + cardBody: React.ReactNode; + /** Content passed into tool response card title */ + cardTitle: React.ReactNode; + /** Additional props passed to main card */ + cardProps?: CardProps; + /** Additional props passed to main card body */ + cardBodyProps?: CardBodyProps; + /** Additional props passed to tool response card */ + toolResponseCardProps?: CardProps; + /** Additional props passed to tool response card body */ + toolResponseCardBodyProps?: CardBodyProps; + /** Additional props passed to tool response card divider */ + toolResponseCardDividerProps?: DividerProps; + /** Additional props passed to tool response card title */ + toolResponseCardTitleProps?: CardTitleProps; +} + +export const ToolResponse: FunctionComponent = ({ + body, + cardProps, + collapsedToggleText, + expandableSectionProps, + expandedToggleText, + subheading, + cardBody, + cardTitle, + cardBodyProps, + toolResponseCardBodyProps, + toolResponseCardDividerProps, + toolResponseCardProps, + toolResponseCardTitleProps +}: ToolResponseProps) => { + const [isExpanded, setIsExpanded] = useState(true); + + const onToggle = (_event: React.MouseEvent, isExpanded: boolean) => { + setIsExpanded(isExpanded); + }; + + return ( + + + +
+ {subheading && ( +
+ {subheading} +
+ )} + {body &&
{body}
} + + {cardTitle} + + {cardBody} + +
+
+
+
+ ); +}; + +export default ToolResponse; diff --git a/packages/module/src/ToolResponse/index.ts b/packages/module/src/ToolResponse/index.ts new file mode 100644 index 000000000..343a49848 --- /dev/null +++ b/packages/module/src/ToolResponse/index.ts @@ -0,0 +1,3 @@ +export { default } from './ToolResponse'; + +export * from './ToolResponse'; diff --git a/packages/module/src/index.ts b/packages/module/src/index.ts index 98167b41d..b80359696 100644 --- a/packages/module/src/index.ts +++ b/packages/module/src/index.ts @@ -84,5 +84,8 @@ export * from './SourcesCard'; export { default as TermsOfUse } from './TermsOfUse'; export * from './TermsOfUse'; +export { default as ToolResponse } from './ToolResponse'; +export * from './ToolResponse'; + export { default as tracking } from './tracking'; export * from './tracking'; diff --git a/packages/module/src/main.scss b/packages/module/src/main.scss index 699a59bd1..8be2f9622 100644 --- a/packages/module/src/main.scss +++ b/packages/module/src/main.scss @@ -33,6 +33,7 @@ @import './SourcesCard/SourcesCard.scss'; @import './SourceDetailsMenuItem/SourceDetailsMenuItem'; @import './TermsOfUse/TermsOfUse'; +@import './ToolResponse/ToolResponse'; .ws-full-page-utils { left: 0 !important; From ff6a829ba9d292450c9383355df6d228fdded942 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Tue, 2 Sep 2025 10:20:39 -0400 Subject: [PATCH 2/6] Update text Co-authored-by: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> --- .../Messages/MessageWithToolResponse.tsx | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx index b78ac541c..b491728a3 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx @@ -29,10 +29,10 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { avatar={patternflyAvatar} content="This example has a body description that's within the recommended limit of 2 lines:" toolResponse={{ - collapsedToggleText: 'Tool response: Name', - expandedToggleText: 'Tool response: Name', + collapsedToggleText: 'Tool response: toolName', + expandedToggleText: 'Tool response: toolName', subheading: 'Thought for 3 seconds', - body: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + body: "Here's the summary for your Tool name response:", cardTitle: ( @@ -48,7 +48,7 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { Execution time: - 0.12s + 0.12 seconds @@ -74,7 +74,7 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { Parameters - Optional description text goes here + Optional description text for parameters. @@ -121,14 +121,8 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { } as any } > - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque nec dignissim turpis, et tristique - purus. Phasellus efficitur ante quis dolor viverra imperdiet. Orci varius natoque penatibus et - magnis dis parturient montes, nascetur ridiculus mus. Pellentesque laoreet, sem ac elementum semper, - lectus mauris vestibulum nulla, eget volutpat massa neque vel turpis. Donec finibus enim eu leo - accumsan consectetur. Praesent massa diam, tincidunt eu dui ac, ullamcorper elementum est. Phasellus - metus felis, venenatis vitae semper nec, porta a metus. Vestibulum justo nisi, imperdiet id eleifend - at, varius nec lorem. Fusce porttitor mollis nibh, ut elementum ante commodo tincidunt. Integer - tincidunt at ipsum non aliquet. + Descriptive text about the tool response, including completion status, details on the data that was + processed, or anything else relevant to the use case. From 08a388cadfd7837e0524dac3c034ff5c5218b0a4 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Thu, 4 Sep 2025 10:25:46 -0400 Subject: [PATCH 3/6] Address spacing --- packages/module/src/ToolResponse/ToolResponse.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/module/src/ToolResponse/ToolResponse.scss b/packages/module/src/ToolResponse/ToolResponse.scss index 7e8e12172..6145cfdc1 100644 --- a/packages/module/src/ToolResponse/ToolResponse.scss +++ b/packages/module/src/ToolResponse/ToolResponse.scss @@ -20,6 +20,7 @@ .pf-chatbot__tool-response-body { color: var(--pf-t--global--text--color--subtle); + margin-block-end: var(--pf-t--global--spacer--xs); } .pf-chatbot__tool-response-card { From f406977d516226186863fd65844e31e59374df1c Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Thu, 4 Sep 2025 10:38:45 -0400 Subject: [PATCH 4/6] Adjust overflow --- packages/module/src/ToolResponse/ToolResponse.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/module/src/ToolResponse/ToolResponse.scss b/packages/module/src/ToolResponse/ToolResponse.scss index 6145cfdc1..b6f71193b 100644 --- a/packages/module/src/ToolResponse/ToolResponse.scss +++ b/packages/module/src/ToolResponse/ToolResponse.scss @@ -1,5 +1,6 @@ .pf-chatbot__tool-response { --pf-v6-c-card--BorderColor: var(--pf-t--global--border--color--control--read-only); + overflow: unset; } .pf-chatbot__tool-response-expandable-section { From 4a42286c121687994d8d1cf6666c2d3e915ed503 Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Thu, 4 Sep 2025 12:43:48 -0400 Subject: [PATCH 5/6] Re-add Erin's changes --- .../chatbot/examples/Messages/MessageWithToolResponse.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx index b491728a3..d97834d9b 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx @@ -32,7 +32,7 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { collapsedToggleText: 'Tool response: toolName', expandedToggleText: 'Tool response: toolName', subheading: 'Thought for 3 seconds', - body: "Here's the summary for your Tool name response:", + body: "Here's the summary for your toolName response:", cardTitle: ( @@ -42,7 +42,7 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { - Tool name + toolName From 52b3730cd133f2bc4e78fa2e7d7c9ecd41bafa6d Mon Sep 17 00:00:00 2001 From: Rebecca Alpert Date: Thu, 4 Sep 2025 14:01:52 -0400 Subject: [PATCH 6/6] Address Eric's comments --- .../Messages/MessageWithToolResponse.tsx | 6 ++--- packages/module/src/Message/Message.test.tsx | 3 +-- .../src/ToolResponse/ToolResponse.test.tsx | 27 ++----------------- .../module/src/ToolResponse/ToolResponse.tsx | 11 +++----- 4 files changed, 10 insertions(+), 37 deletions(-) diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx index d97834d9b..6ae583dd0 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx @@ -29,8 +29,7 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { avatar={patternflyAvatar} content="This example has a body description that's within the recommended limit of 2 lines:" toolResponse={{ - collapsedToggleText: 'Tool response: toolName', - expandedToggleText: 'Tool response: toolName', + toggleContent: 'Tool response: toolName', subheading: 'Thought for 3 seconds', body: "Here's the summary for your toolName response:", cardTitle: ( @@ -109,7 +108,8 @@ export const MessageWithToolResponseExample: FunctionComponent = () => { { name="User" content="Hi" toolResponse={{ - collapsedToggleText: 'Tool response: Name', - expandedToggleText: 'Tool response: Name', + toggleContent: 'Tool response: Name', subheading: 'Thought for 3 seconds', body: 'Lorem ipsum dolor sit amet', cardTitle: 'Card title', diff --git a/packages/module/src/ToolResponse/ToolResponse.test.tsx b/packages/module/src/ToolResponse/ToolResponse.test.tsx index aaefa51bc..ef65873e8 100644 --- a/packages/module/src/ToolResponse/ToolResponse.test.tsx +++ b/packages/module/src/ToolResponse/ToolResponse.test.tsx @@ -1,12 +1,10 @@ import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; import '@testing-library/jest-dom'; import ToolResponse from './ToolResponse'; describe('ToolResponse', () => { const defaultProps = { - collapsedToggleText: 'Show details', - expandedToggleText: 'Hide details', + toggleContent: 'Tool response: toolName', cardTitle: 'Title', cardBody: 'Body' }; @@ -15,28 +13,7 @@ describe('ToolResponse', () => { render(); expect(screen.getByText('Title')).toBeTruthy(); expect(screen.getByText('Body')).toBeTruthy(); - expect(screen.getByText('Hide details')).toBeTruthy(); - }); - - it('should render expanded by default', () => { - render(); - expect(screen.getByText('Hide details')).toBeTruthy(); - expect(screen.queryByText('Show details')).toBeFalsy(); - }); - - it('should toggle between expanded and collapsed states', async () => { - const user = userEvent.setup(); - render(); - const toggleButton = screen.getByRole('button', { name: /Hide details/i }); - expect(screen.getByText('Hide details')).toBeTruthy(); - await user.click(toggleButton); - expect(screen.getByText('Show details')).toBeTruthy(); - expect(screen.queryByText('Hide details')).toBeFalsy(); - - // Click to expand again - await user.click(screen.getByRole('button', { name: /Show details/i })); - expect(screen.getByText('Hide details')).toBeTruthy(); - expect(screen.queryByText('Show details')).toBeFalsy(); + expect(screen.getByText('Tool response: toolName')).toBeTruthy(); }); it('should render subheading when provided', () => { diff --git a/packages/module/src/ToolResponse/ToolResponse.tsx b/packages/module/src/ToolResponse/ToolResponse.tsx index 06ced47ea..62eb8bd3e 100644 --- a/packages/module/src/ToolResponse/ToolResponse.tsx +++ b/packages/module/src/ToolResponse/ToolResponse.tsx @@ -16,10 +16,8 @@ import { import { useState, type FunctionComponent } from 'react'; export interface ToolResponseProps { - /** Toggle text shown on expandable section when it is collapsed */ - collapsedToggleText: string; - /** Toggle text shown on expandable section when it is expanded */ - expandedToggleText: string; + /** Toggle content shown for expandable section */ + toggleContent: React.ReactNode; /** Additional props passed to expandable section */ expandableSectionProps?: Omit; /** Subheading rendered inside expandable section */ @@ -47,13 +45,12 @@ export interface ToolResponseProps { export const ToolResponse: FunctionComponent = ({ body, cardProps, - collapsedToggleText, expandableSectionProps, - expandedToggleText, subheading, cardBody, cardTitle, cardBodyProps, + toggleContent, toolResponseCardBodyProps, toolResponseCardDividerProps, toolResponseCardProps, @@ -69,7 +66,7 @@ export const ToolResponse: FunctionComponent = ({