-
Notifications
You must be signed in to change notification settings - Fork 37
feat(ToolResponse): Add component #662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
b54d4cb
feat(ToolResponse): Add component
rebeccaalpert ff6a829
Update text
rebeccaalpert 08a388c
Address spacing
rebeccaalpert f406977
Adjust overflow
rebeccaalpert 4a42286
Re-add Erin's changes
rebeccaalpert 52b3730
Address Eric's comments
rebeccaalpert File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
135 changes: 135 additions & 0 deletions
135
.../patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| 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 ( | ||
| <Message | ||
| name="Bot" | ||
| role="bot" | ||
| avatar={patternflyAvatar} | ||
| content="This example has a body description that's within the recommended limit of 2 lines:" | ||
| toolResponse={{ | ||
| toggleContent: 'Tool response: toolName', | ||
| subheading: 'Thought for 3 seconds', | ||
| body: "Here's the summary for your toolName response:", | ||
| cardTitle: ( | ||
| <Flex alignItems={{ default: 'alignItemsCenter' }} justifyContent={{ default: 'justifyContentSpaceBetween' }}> | ||
| <FlexItem> | ||
| <Flex direction={{ default: 'column' }} gap={{ default: 'gapXs' }}> | ||
| <FlexItem grow={{ default: 'grow' }}> | ||
| <Flex gap={{ default: 'gapXs' }}> | ||
| <FlexItem> | ||
| <WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} /> | ||
| </FlexItem> | ||
| <FlexItem>toolName</FlexItem> | ||
| </Flex> | ||
| </FlexItem> | ||
| <FlexItem> | ||
| <Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}> | ||
| <FlexItem>Execution time:</FlexItem> | ||
| <FlexItem>0.12 seconds</FlexItem> | ||
| </Flex> | ||
| </FlexItem> | ||
| </Flex> | ||
| </FlexItem> | ||
| <FlexItem> | ||
| <Button | ||
| variant="plain" | ||
| aria-label="Copy tool response to clipboard" | ||
| icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />} | ||
| ></Button> | ||
| </FlexItem> | ||
| </Flex> | ||
| ), | ||
| cardBody: ( | ||
| <> | ||
| <DescriptionList | ||
| style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any} | ||
| aria-label="Tool response" | ||
| > | ||
| <DescriptionListGroup | ||
| style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any} | ||
| > | ||
| <DescriptionListTerm>Parameters</DescriptionListTerm> | ||
| <DescriptionListDescription> | ||
| <Flex direction={{ default: 'column' }}> | ||
| <FlexItem>Optional description text for parameters.</FlexItem> | ||
| <FlexItem> | ||
| <Flex gap={{ default: 'gapSm' }}> | ||
| <FlexItem> | ||
| <Label variant="outline" color="blue"> | ||
| type | ||
| </Label> | ||
| </FlexItem> | ||
| <FlexItem> | ||
| <Label variant="outline" color="blue"> | ||
| properties | ||
| </Label> | ||
| </FlexItem> | ||
| <FlexItem> | ||
| <Label variant="outline" color="blue"> | ||
| label | ||
| </Label> | ||
| </FlexItem> | ||
| <FlexItem> | ||
| <Label variant="outline" color="blue"> | ||
| label | ||
| </Label> | ||
| </FlexItem> | ||
| </Flex> | ||
| </FlexItem> | ||
| </Flex> | ||
| </DescriptionListDescription> | ||
| </DescriptionListGroup> | ||
| <DescriptionListGroup | ||
| style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any} | ||
| > | ||
| <DescriptionListTerm>Response</DescriptionListTerm> | ||
| <DescriptionListDescription> | ||
| <ExpandableSection | ||
| variant={ExpandableSectionVariant.truncate} | ||
| toggleTextExpanded="show less of response" | ||
| toggleTextCollapsed="show more of response" | ||
| onToggle={onToggle} | ||
| isExpanded={isExpanded} | ||
| style={ | ||
| { | ||
| '--pf-v6-c-expandable-section__content--Opacity': '1', | ||
| '--pf-v6-c-expandable-section__content--PaddingInlineStart': 0, | ||
| '--pf-v6-c-expandable-section__content--TranslateY': 0, | ||
| '--pf-v6-c-expandable-section--m-expand-top__content--TranslateY': 0 | ||
| } as any | ||
| } | ||
| > | ||
| Descriptive text about the tool response, including completion status, details on the data that was | ||
| processed, or anything else relevant to the use case. | ||
| </ExpandableSection> | ||
| </DescriptionListDescription> | ||
| </DescriptionListGroup> | ||
| </DescriptionList> | ||
| </> | ||
| ) | ||
| }} | ||
| /> | ||
| ); | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| .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 { | ||
| --pf-v6-c-expandable-section--Gap: var(--pf-t--global--spacer--xs); | ||
| } | ||
|
|
||
| .pf-chatbot__tool-response-section { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: var(--pf-t--global--spacer--xs); | ||
| } | ||
|
|
||
| .pf-chatbot__tool-response-subheading { | ||
| font-size: var(--pf-t--global--font--size--body--sm); | ||
| font-weight: var(--pf-t--global--font--weight--body--default); | ||
| color: var(--pf-t--global--text--color--subtle); | ||
| } | ||
|
|
||
| .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 { | ||
| --pf-v6-c-card--BorderColor: var(--pf-t--global--border--color--control--read-only); | ||
| --pf-v6-c-card--first-child--PaddingBlockStart: var(--pf-t--global--spacer--sm); | ||
| --pf-v6-c-card__title--not--last-child--PaddingBlockEnd: var(--pf-t--global--spacer--sm); | ||
| --pf-v6-c-card--c-divider--child--PaddingBlockStart: var(--pf-t--global--spacer--sm); | ||
|
|
||
| .pf-v6-c-divider { | ||
| --pf-v6-c-divider--Color: var(--pf-t--global--border--color--control--read-only); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| import { render, screen } from '@testing-library/react'; | ||
| import '@testing-library/jest-dom'; | ||
| import ToolResponse from './ToolResponse'; | ||
|
|
||
| describe('ToolResponse', () => { | ||
| const defaultProps = { | ||
| toggleContent: 'Tool response: toolName', | ||
| cardTitle: 'Title', | ||
| cardBody: 'Body' | ||
| }; | ||
|
|
||
| it('should render with required props only', () => { | ||
| render(<ToolResponse {...defaultProps} />); | ||
| expect(screen.getByText('Title')).toBeTruthy(); | ||
| expect(screen.getByText('Body')).toBeTruthy(); | ||
| expect(screen.getByText('Tool response: toolName')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should render subheading when provided', () => { | ||
| const subheading = 'Tool execution result'; | ||
| render(<ToolResponse {...defaultProps} subheading={subheading} />); | ||
| expect(screen.getByText(subheading)).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should render body content when provided', () => { | ||
| const body = 'This is the tool response body content'; | ||
| render(<ToolResponse {...defaultProps} body={body} />); | ||
| expect(screen.getByText(body)).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should render with complex content including React elements', () => { | ||
| const body = ( | ||
| <div> | ||
| <p>Complex body content</p> | ||
| <ul> | ||
| <li>Item 1</li> | ||
| <li>Item 2</li> | ||
| </ul> | ||
| </div> | ||
| ); | ||
| const cardTitle = <strong>API Response</strong>; | ||
| const cardBody = ( | ||
| <div> | ||
| <code>{"{ status: 'success' }"}</code> | ||
| </div> | ||
| ); | ||
|
|
||
| render(<ToolResponse {...defaultProps} body={body} cardTitle={cardTitle} cardBody={cardBody} />); | ||
| 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( | ||
| <ToolResponse {...defaultProps} cardProps={{ className: 'custom-tool-response-class' }} /> | ||
| ); | ||
| expect(container.querySelector('.custom-tool-response-class')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should pass through expandableSectionProps', () => { | ||
| render(<ToolResponse {...defaultProps} expandableSectionProps={{ className: 'custom-expandable-class' }} />); | ||
| expect(document.querySelector('.custom-expandable-class')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should pass through toolResponseCardProps', () => { | ||
| render(<ToolResponse {...defaultProps} toolResponseCardProps={{ className: 'custom-card-class' }} />); | ||
| expect(document.querySelector('.custom-card-class')).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('should not render subheading span when subheading is not provided', () => { | ||
| const { container } = render(<ToolResponse {...defaultProps} />); | ||
| const subheadingContainer = container.querySelector('.pf-chatbot__tool-response-subheading'); | ||
| expect(subheadingContainer).toBeFalsy(); | ||
| }); | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See patternfly/patternfly#7767.