@patternfly/chatbot 6.4.0-prerelease.21 → 6.4.0-prerelease.23
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/Message/Message.d.ts +3 -0
- package/dist/cjs/Message/Message.js +3 -2
- package/dist/cjs/ToolCall/ToolCall.d.ts +44 -0
- package/dist/cjs/ToolCall/ToolCall.js +14 -0
- package/dist/cjs/ToolCall/ToolCall.test.d.ts +1 -0
- package/dist/cjs/ToolCall/ToolCall.test.js +144 -0
- package/dist/cjs/ToolCall/index.d.ts +2 -0
- package/dist/cjs/ToolCall/index.js +23 -0
- package/dist/cjs/ToolResponse/ToolResponse.d.ts +2 -2
- package/dist/cjs/ToolResponse/ToolResponse.js +1 -1
- package/dist/cjs/ToolResponse/ToolResponse.test.js +24 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/css/main.css +27 -3
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/ToolCall/package.json +1 -0
- package/dist/esm/Message/Message.d.ts +3 -0
- package/dist/esm/Message/Message.js +3 -2
- package/dist/esm/ToolCall/ToolCall.d.ts +44 -0
- package/dist/esm/ToolCall/ToolCall.js +10 -0
- package/dist/esm/ToolCall/ToolCall.test.d.ts +1 -0
- package/dist/esm/ToolCall/ToolCall.test.js +139 -0
- package/dist/esm/ToolCall/index.d.ts +2 -0
- package/dist/esm/ToolCall/index.js +2 -0
- package/dist/esm/ToolResponse/ToolResponse.d.ts +2 -2
- package/dist/esm/ToolResponse/ToolResponse.js +1 -1
- package/dist/esm/ToolResponse/ToolResponse.test.js +24 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolCall.tsx +45 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +10 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +1 -1
- package/src/Message/Message.tsx +5 -0
- package/src/ToolCall/ToolCall.scss +37 -0
- package/src/ToolCall/ToolCall.test.tsx +184 -0
- package/src/ToolCall/ToolCall.tsx +147 -0
- package/src/ToolCall/index.ts +3 -0
- package/src/ToolResponse/ToolResponse.test.tsx +30 -0
- package/src/ToolResponse/ToolResponse.tsx +9 -7
- package/src/index.ts +3 -0
- package/src/main.scss +1 -5
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import ToolCall from './ToolCall';
|
|
5
|
+
|
|
6
|
+
describe('ToolCall', () => {
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
titleText: 'ToolCall Title',
|
|
9
|
+
loadingText: 'Loading ToolCall'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
it('Renders with passed in titleText', () => {
|
|
13
|
+
render(<ToolCall {...defaultProps} />);
|
|
14
|
+
expect(screen.getByText(defaultProps.titleText)).toBeVisible();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('Does not render with passed in loadingText when isLoading is false', () => {
|
|
18
|
+
render(<ToolCall {...defaultProps} />);
|
|
19
|
+
expect(screen.queryByText(defaultProps.loadingText)).not.toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('Renders with passed in loadingText when isLoading is true', () => {
|
|
23
|
+
render(<ToolCall {...defaultProps} isLoading />);
|
|
24
|
+
expect(screen.getByText(defaultProps.loadingText)).toBeVisible();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('Does not render titleText when isLoading is true', () => {
|
|
28
|
+
render(<ToolCall {...defaultProps} isLoading />);
|
|
29
|
+
expect(screen.queryByText(defaultProps.titleText)).not.toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('Passes spinnerProps to Spinner', () => {
|
|
33
|
+
render(<ToolCall {...defaultProps} isLoading spinnerProps={{ id: 'spinner-test-id' }} />);
|
|
34
|
+
|
|
35
|
+
expect(screen.getByRole('progressbar')).toHaveAttribute('id', 'spinner-test-id');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('Does not render expandable toggle by default', () => {
|
|
39
|
+
render(<ToolCall {...defaultProps} />);
|
|
40
|
+
expect(screen.queryByRole('button', { name: defaultProps.titleText })).not.toBeInTheDocument();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('Renders titleText inside expandable toggle when expandableContent is passed', () => {
|
|
44
|
+
render(<ToolCall {...defaultProps} expandableContent="Expandable Content" />);
|
|
45
|
+
expect(screen.getByRole('button', { name: defaultProps.titleText })).toBeVisible();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('Does not render expandable content when expandableContent is passed by default', () => {
|
|
49
|
+
render(<ToolCall {...defaultProps} expandableContent="Expandable Content" />);
|
|
50
|
+
expect(screen.queryByText('Expandable Content')).not.toBeVisible();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('Renders expandable content when expandableContent is passed and toggle is clicked', async () => {
|
|
54
|
+
const user = userEvent.setup();
|
|
55
|
+
render(<ToolCall {...defaultProps} expandableContent="Expandable Content" />);
|
|
56
|
+
await user.click(screen.getByRole('button', { name: defaultProps.titleText }));
|
|
57
|
+
|
|
58
|
+
expect(screen.getByText('Expandable Content')).toBeVisible();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('Passes expandableSectionProps to ExpandableSection', () => {
|
|
62
|
+
render(
|
|
63
|
+
<ToolCall
|
|
64
|
+
{...defaultProps}
|
|
65
|
+
expandableContent="Expandable Content"
|
|
66
|
+
expandableSectionProps={{ id: 'expandable-section-test-id', isExpanded: true }}
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
expect(screen.getByRole('region').parentElement).toHaveAttribute('id', 'expandable-section-test-id');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('Renders "run" action button by default', () => {
|
|
73
|
+
render(<ToolCall {...defaultProps} />);
|
|
74
|
+
expect(screen.getByRole('button', { name: 'Run tool' })).toBeVisible();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('Renders "cancel" action button by default', () => {
|
|
78
|
+
render(<ToolCall {...defaultProps} />);
|
|
79
|
+
expect(screen.getByRole('button', { name: 'Cancel' })).toBeVisible();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('Does not render "run" action button when isLoading is true', () => {
|
|
83
|
+
render(<ToolCall {...defaultProps} isLoading />);
|
|
84
|
+
expect(screen.queryByRole('button', { name: 'Run tool' })).not.toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('Does not render "cancel" action button when isLoading is true', () => {
|
|
88
|
+
render(<ToolCall {...defaultProps} isLoading />);
|
|
89
|
+
expect(screen.queryByRole('button', { name: 'Cancel' })).not.toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('Renders runButtonText when passed', () => {
|
|
93
|
+
render(<ToolCall {...defaultProps} runButtonText="Run my custom tool" />);
|
|
94
|
+
expect(screen.getByRole('button', { name: 'Run my custom tool' })).toBeVisible();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('Renders cancelButtonText when passed', () => {
|
|
98
|
+
render(<ToolCall {...defaultProps} cancelButtonText="Cancel my custom tool" />);
|
|
99
|
+
expect(screen.getByRole('button', { name: 'Cancel my custom tool' })).toBeVisible();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('Passes runButtonProps to Button', () => {
|
|
103
|
+
render(<ToolCall {...defaultProps} runButtonProps={{ id: 'run-button-test-id' }} />);
|
|
104
|
+
expect(screen.getByRole('button', { name: 'Run tool' })).toHaveAttribute('id', 'run-button-test-id');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('Passes cancelButtonProps to Button', () => {
|
|
108
|
+
render(<ToolCall {...defaultProps} cancelButtonProps={{ id: 'cancel-button-test-id' }} />);
|
|
109
|
+
expect(screen.getByRole('button', { name: 'Cancel' })).toHaveAttribute('id', 'cancel-button-test-id');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('Passes runActionItemProps to ActionListItem', () => {
|
|
113
|
+
render(<ToolCall {...defaultProps} runActionItemProps={{ id: 'run-action-item-test-id' }} />);
|
|
114
|
+
expect(screen.getByRole('button', { name: 'Run tool' }).parentElement).toHaveAttribute(
|
|
115
|
+
'id',
|
|
116
|
+
'run-action-item-test-id'
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('Passes cancelActionItemProps to ActionListItem', () => {
|
|
121
|
+
render(<ToolCall {...defaultProps} cancelActionItemProps={{ id: 'cancel-action-item-test-id' }} />);
|
|
122
|
+
expect(screen.getByRole('button', { name: 'Cancel' }).parentElement).toHaveAttribute(
|
|
123
|
+
'id',
|
|
124
|
+
'cancel-action-item-test-id'
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('Passes actionListProps to ActionList', () => {
|
|
129
|
+
render(<ToolCall {...defaultProps} actionListProps={{ id: 'action-list-test-id' }} />);
|
|
130
|
+
expect(screen.getByRole('button', { name: 'Run tool' }).closest('#action-list-test-id')).toBeVisible();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('Passes actionListGroupProps to ActionListGroup', () => {
|
|
134
|
+
render(<ToolCall {...defaultProps} actionListGroupProps={{ id: 'action-list-group-test-id' }} />);
|
|
135
|
+
expect(screen.getByRole('button', { name: 'Run tool' }).closest('#action-list-group-test-id')).toBeVisible();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('Passes actionListItemProps to ActionListItem for default actions', () => {
|
|
139
|
+
render(<ToolCall {...defaultProps} actionListItemProps={{ className: 'action-list-item-test-class' }} />);
|
|
140
|
+
expect(screen.getByRole('button', { name: 'Run tool' }).parentElement).toHaveClass('action-list-item-test-class');
|
|
141
|
+
expect(screen.getByRole('button', { name: 'Cancel' }).parentElement).toHaveClass('action-list-item-test-class');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('Renders custom actions instead of default actions when actions are passed', () => {
|
|
145
|
+
render(
|
|
146
|
+
<ToolCall
|
|
147
|
+
{...defaultProps}
|
|
148
|
+
actions={[<div key="custom-action-1">Custom action 1</div>, <div key="custom-action-2">Custom action 2</div>]}
|
|
149
|
+
/>
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
expect(screen.getByText('Custom action 1')).toBeVisible();
|
|
153
|
+
expect(screen.getByText('Custom action 2')).toBeVisible();
|
|
154
|
+
expect(screen.queryByRole('button', { name: 'Run tool' })).not.toBeInTheDocument();
|
|
155
|
+
expect(screen.queryByRole('button', { name: 'Cancel' })).not.toBeInTheDocument();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('Passes actionListItemProps to ActionListItem for custom actions', () => {
|
|
159
|
+
render(
|
|
160
|
+
<ToolCall
|
|
161
|
+
{...defaultProps}
|
|
162
|
+
actions={[<div key="custom-action-1">Custom action 1</div>, <div key="custom-action-2">Custom action 2</div>]}
|
|
163
|
+
actionListItemProps={{ className: 'action-list-item-test-class' }}
|
|
164
|
+
/>
|
|
165
|
+
);
|
|
166
|
+
expect(screen.getByText('Custom action 1').parentElement).toHaveClass('action-list-item-test-class');
|
|
167
|
+
expect(screen.getByText('Custom action 2').parentElement).toHaveClass('action-list-item-test-class');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('Passes cardProps to Card', () => {
|
|
171
|
+
render(<ToolCall {...defaultProps} cardProps={{ id: 'card-test-id' }} />);
|
|
172
|
+
expect(screen.getByRole('button', { name: 'Run tool' }).closest('#card-test-id')).toBeVisible();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('Passes cardBodyProps to CardBody', () => {
|
|
176
|
+
render(<ToolCall {...defaultProps} cardBodyProps={{ id: 'card-body-test-id' }} />);
|
|
177
|
+
expect(screen.getByText(defaultProps.titleText).closest('#card-body-test-id')).toBeVisible();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('Passes cardFooterProps to CardFooter', () => {
|
|
181
|
+
render(<ToolCall {...defaultProps} cardFooterProps={{ id: 'card-footer-test-id' }} />);
|
|
182
|
+
expect(screen.getByRole('button', { name: 'Run tool' }).closest('#card-footer-test-id')).toBeVisible();
|
|
183
|
+
});
|
|
184
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type FunctionComponent } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
ActionList,
|
|
4
|
+
ActionListProps,
|
|
5
|
+
ActionListGroup,
|
|
6
|
+
ActionListGroupProps,
|
|
7
|
+
ActionListItem,
|
|
8
|
+
ActionListItemProps,
|
|
9
|
+
Button,
|
|
10
|
+
ButtonProps,
|
|
11
|
+
Card,
|
|
12
|
+
CardProps,
|
|
13
|
+
CardBody,
|
|
14
|
+
CardBodyProps,
|
|
15
|
+
CardFooter,
|
|
16
|
+
CardFooterProps,
|
|
17
|
+
ExpandableSection,
|
|
18
|
+
ExpandableSectionProps,
|
|
19
|
+
Spinner,
|
|
20
|
+
SpinnerProps
|
|
21
|
+
} from '@patternfly/react-core';
|
|
22
|
+
|
|
23
|
+
export interface ToolCallProps {
|
|
24
|
+
/** Title text for the tool call. */
|
|
25
|
+
titleText: string;
|
|
26
|
+
/** Loading text for the tool call. */
|
|
27
|
+
loadingText?: string;
|
|
28
|
+
/** Flag indicating whether the tool call is loading or not. */
|
|
29
|
+
isLoading?: boolean;
|
|
30
|
+
/** Additional props for the spinner that is rendered when isLoading is true. */
|
|
31
|
+
spinnerProps?: SpinnerProps;
|
|
32
|
+
/** Content to render within an expandable section. */
|
|
33
|
+
expandableContent?: React.ReactNode;
|
|
34
|
+
/** Text content for the "run" action button. */
|
|
35
|
+
runButtonText?: string;
|
|
36
|
+
/** Additional props for the "run" action button. */
|
|
37
|
+
runButtonProps?: ButtonProps;
|
|
38
|
+
/** Additional props for the "run" action list item. */
|
|
39
|
+
runActionItemProps?: ActionListItemProps;
|
|
40
|
+
/** Text content for the "cancel" action button. */
|
|
41
|
+
cancelButtonText?: string;
|
|
42
|
+
/** Additional props for the "cancel" action button. */
|
|
43
|
+
cancelButtonProps?: ButtonProps;
|
|
44
|
+
/** Additional props for the "cancel" action list item. */
|
|
45
|
+
cancelActionItemProps?: ActionListItemProps;
|
|
46
|
+
/** Custom actions to render, typically a "cancel" and "run" action. This will override the default actions. */
|
|
47
|
+
actions?: React.ReactNode[];
|
|
48
|
+
/** Additional props for the action list */
|
|
49
|
+
actionListProps?: ActionListProps;
|
|
50
|
+
/** Additional props for the action list group. */
|
|
51
|
+
actionListGroupProps?: ActionListGroupProps;
|
|
52
|
+
/** Additional props for all action list items. */
|
|
53
|
+
actionListItemProps?: ActionListItemProps;
|
|
54
|
+
/** Additional props for the card. */
|
|
55
|
+
cardProps?: CardProps;
|
|
56
|
+
/** Additional props for the card body that contains the main tool call content. */
|
|
57
|
+
cardBodyProps?: CardBodyProps;
|
|
58
|
+
/** Additional props for the card footer that contains the tool call actions. */
|
|
59
|
+
cardFooterProps?: CardFooterProps;
|
|
60
|
+
/** Additional props for the expandable section when expandableContent is passed. */
|
|
61
|
+
expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const ToolCall: FunctionComponent<ToolCallProps> = ({
|
|
65
|
+
titleText,
|
|
66
|
+
loadingText,
|
|
67
|
+
isLoading,
|
|
68
|
+
expandableContent,
|
|
69
|
+
runButtonText = 'Run tool',
|
|
70
|
+
runButtonProps,
|
|
71
|
+
runActionItemProps,
|
|
72
|
+
cancelButtonText = 'Cancel',
|
|
73
|
+
cancelButtonProps,
|
|
74
|
+
cancelActionItemProps,
|
|
75
|
+
actions,
|
|
76
|
+
actionListProps,
|
|
77
|
+
actionListGroupProps,
|
|
78
|
+
actionListItemProps,
|
|
79
|
+
cardProps,
|
|
80
|
+
cardBodyProps,
|
|
81
|
+
cardFooterProps,
|
|
82
|
+
expandableSectionProps,
|
|
83
|
+
spinnerProps
|
|
84
|
+
}: ToolCallProps) => {
|
|
85
|
+
const titleContent = (
|
|
86
|
+
<span className={`pf-chatbot__tool-call-title-content`}>
|
|
87
|
+
{isLoading ? (
|
|
88
|
+
<>
|
|
89
|
+
<Spinner diameter="1em" {...spinnerProps} />{' '}
|
|
90
|
+
{<span className="pf-chatbot__tool-call-title-text">{loadingText}</span>}
|
|
91
|
+
</>
|
|
92
|
+
) : (
|
|
93
|
+
<span className="pf-chatbot__tool-call-title-text">{titleText}</span>
|
|
94
|
+
)}
|
|
95
|
+
</span>
|
|
96
|
+
);
|
|
97
|
+
const defaultActions = (
|
|
98
|
+
<>
|
|
99
|
+
<ActionListItem {...actionListItemProps} {...cancelActionItemProps}>
|
|
100
|
+
<Button variant="link" {...cancelButtonProps}>
|
|
101
|
+
{cancelButtonText}
|
|
102
|
+
</Button>
|
|
103
|
+
</ActionListItem>
|
|
104
|
+
<ActionListItem {...actionListItemProps} {...runActionItemProps}>
|
|
105
|
+
<Button variant="secondary" {...runButtonProps}>
|
|
106
|
+
{runButtonText}
|
|
107
|
+
</Button>
|
|
108
|
+
</ActionListItem>
|
|
109
|
+
</>
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const customActions =
|
|
113
|
+
actions &&
|
|
114
|
+
actions.map((action, index) => (
|
|
115
|
+
<ActionListItem key={index} {...actionListItemProps}>
|
|
116
|
+
{action}
|
|
117
|
+
</ActionListItem>
|
|
118
|
+
));
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<Card isCompact className="pf-chatbot__tool-call" {...cardProps}>
|
|
122
|
+
<CardBody className="pf-chatbot__tool-call-title" {...cardBodyProps}>
|
|
123
|
+
{expandableContent && !isLoading ? (
|
|
124
|
+
<ExpandableSection
|
|
125
|
+
className="pf-chatbot__tool-call-expandable-section"
|
|
126
|
+
toggleContent={titleContent}
|
|
127
|
+
isIndented
|
|
128
|
+
{...expandableSectionProps}
|
|
129
|
+
>
|
|
130
|
+
{expandableContent}
|
|
131
|
+
</ExpandableSection>
|
|
132
|
+
) : (
|
|
133
|
+
titleContent
|
|
134
|
+
)}
|
|
135
|
+
</CardBody>
|
|
136
|
+
{!isLoading && (
|
|
137
|
+
<CardFooter {...cardFooterProps}>
|
|
138
|
+
<ActionList className="pf-chatbot__tool-call-action-list" {...actionListProps}>
|
|
139
|
+
<ActionListGroup {...actionListGroupProps}>{customActions || defaultActions}</ActionListGroup>
|
|
140
|
+
</ActionList>
|
|
141
|
+
</CardFooter>
|
|
142
|
+
)}
|
|
143
|
+
</Card>
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export default ToolCall;
|
|
@@ -75,4 +75,34 @@ describe('ToolResponse', () => {
|
|
|
75
75
|
const subheadingContainer = container.querySelector('.pf-chatbot__tool-response-subheading');
|
|
76
76
|
expect(subheadingContainer).toBeFalsy();
|
|
77
77
|
});
|
|
78
|
+
|
|
79
|
+
it('should not render card when cardTitle and cardBody are not provided', () => {
|
|
80
|
+
const { container } = render(<ToolResponse {...defaultProps} cardTitle={undefined} cardBody={undefined} />);
|
|
81
|
+
expect(container.querySelector('.pf-chatbot__tool-response-card')).toBeFalsy();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should render card when only cardTitle is provided', () => {
|
|
85
|
+
const { container } = render(<ToolResponse {...defaultProps} cardBody={undefined} />);
|
|
86
|
+
expect(container.querySelector('.pf-chatbot__tool-response-card')).toBeTruthy();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should render card when only cardBody is provided', () => {
|
|
90
|
+
const { container } = render(<ToolResponse {...defaultProps} cardTitle={undefined} />);
|
|
91
|
+
expect(container.querySelector('.pf-chatbot__tool-response-card')).toBeTruthy();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should render divider when cardBody and cardTitle are provided', () => {
|
|
95
|
+
const { container } = render(<ToolResponse {...defaultProps} />);
|
|
96
|
+
expect(container.querySelector('.pf-v6-c-divider')).toBeTruthy();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should not render divider when only cardBody is provided', () => {
|
|
100
|
+
const { container } = render(<ToolResponse {...defaultProps} cardTitle={undefined} />);
|
|
101
|
+
expect(container.querySelector('.pf-v6-c-divider')).toBeFalsy();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should not render divider when only cardTitle is provided', () => {
|
|
105
|
+
const { container } = render(<ToolResponse {...defaultProps} cardBody={undefined} />);
|
|
106
|
+
expect(container.querySelector('.pf-v6-c-divider')).toBeFalsy();
|
|
107
|
+
});
|
|
78
108
|
});
|
|
@@ -25,9 +25,9 @@ export interface ToolResponseProps {
|
|
|
25
25
|
/** Body text rendered inside expandable section */
|
|
26
26
|
body?: React.ReactNode | string;
|
|
27
27
|
/** Content passed into tool response card body */
|
|
28
|
-
cardBody
|
|
28
|
+
cardBody?: React.ReactNode;
|
|
29
29
|
/** Content passed into tool response card title */
|
|
30
|
-
cardTitle
|
|
30
|
+
cardTitle?: React.ReactNode;
|
|
31
31
|
/** Additional props passed to main card */
|
|
32
32
|
cardProps?: CardProps;
|
|
33
33
|
/** Additional props passed to main card body */
|
|
@@ -80,11 +80,13 @@ export const ToolResponse: FunctionComponent<ToolResponseProps> = ({
|
|
|
80
80
|
</div>
|
|
81
81
|
)}
|
|
82
82
|
{body && <div className="pf-chatbot__tool-response-body">{body}</div>}
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
{(cardTitle || cardBody) && (
|
|
84
|
+
<Card isCompact className="pf-chatbot__tool-response-card" {...toolResponseCardProps}>
|
|
85
|
+
{cardTitle && <CardTitle {...toolResponseCardTitleProps}>{cardTitle}</CardTitle>}
|
|
86
|
+
{cardTitle && cardBody && <Divider {...toolResponseCardDividerProps} />}
|
|
87
|
+
{cardBody && <CardBody {...toolResponseCardBodyProps}>{cardBody}</CardBody>}
|
|
88
|
+
</Card>
|
|
89
|
+
)}
|
|
88
90
|
</div>
|
|
89
91
|
</ExpandableSection>
|
|
90
92
|
</CardBody>
|
package/src/index.ts
CHANGED
|
@@ -93,6 +93,9 @@ export * from './SourcesCard';
|
|
|
93
93
|
export { default as TermsOfUse } from './TermsOfUse';
|
|
94
94
|
export * from './TermsOfUse';
|
|
95
95
|
|
|
96
|
+
export { default as ToolCall } from './ToolCall';
|
|
97
|
+
export * from './ToolCall';
|
|
98
|
+
|
|
96
99
|
export { default as ToolResponse } from './ToolResponse';
|
|
97
100
|
export * from './ToolResponse';
|
|
98
101
|
|
package/src/main.scss
CHANGED
|
@@ -37,11 +37,7 @@
|
|
|
37
37
|
@import './SourceDetailsMenuItem/SourceDetailsMenuItem';
|
|
38
38
|
@import './TermsOfUse/TermsOfUse';
|
|
39
39
|
@import './ToolResponse/ToolResponse';
|
|
40
|
-
|
|
41
|
-
.ws-full-page-utils {
|
|
42
|
-
left: 0 !important;
|
|
43
|
-
right: auto !important;
|
|
44
|
-
}
|
|
40
|
+
@import './ToolCall/ToolCall';
|
|
45
41
|
|
|
46
42
|
// hide from view but not assistive technologies
|
|
47
43
|
// https://css-tricks.com/inclusively-hidden/
|