@patternfly/chatbot 6.4.0-prerelease.15 → 6.4.0-prerelease.17

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.
Files changed (48) hide show
  1. package/dist/cjs/Message/Message.d.ts +3 -0
  2. package/dist/cjs/Message/Message.js +3 -2
  3. package/dist/cjs/Message/Message.test.js +14 -0
  4. package/dist/cjs/SourcesCard/SourcesCard.d.ts +13 -1
  5. package/dist/cjs/SourcesCard/SourcesCard.js +6 -6
  6. package/dist/cjs/SourcesCard/SourcesCard.test.js +49 -0
  7. package/dist/cjs/ToolResponse/ToolResponse.d.ts +30 -0
  8. package/dist/cjs/ToolResponse/ToolResponse.js +18 -0
  9. package/dist/cjs/ToolResponse/ToolResponse.test.d.ts +1 -0
  10. package/dist/cjs/ToolResponse/ToolResponse.test.js +60 -0
  11. package/dist/cjs/ToolResponse/index.d.ts +2 -0
  12. package/dist/cjs/ToolResponse/index.js +23 -0
  13. package/dist/cjs/index.d.ts +2 -0
  14. package/dist/cjs/index.js +4 -1
  15. package/dist/css/main.css +53 -0
  16. package/dist/css/main.css.map +1 -1
  17. package/dist/dynamic/ToolResponse/package.json +1 -0
  18. package/dist/esm/Message/Message.d.ts +3 -0
  19. package/dist/esm/Message/Message.js +3 -2
  20. package/dist/esm/Message/Message.test.js +14 -0
  21. package/dist/esm/SourcesCard/SourcesCard.d.ts +13 -1
  22. package/dist/esm/SourcesCard/SourcesCard.js +6 -6
  23. package/dist/esm/SourcesCard/SourcesCard.test.js +50 -1
  24. package/dist/esm/ToolResponse/ToolResponse.d.ts +30 -0
  25. package/dist/esm/ToolResponse/ToolResponse.js +14 -0
  26. package/dist/esm/ToolResponse/ToolResponse.test.d.ts +1 -0
  27. package/dist/esm/ToolResponse/ToolResponse.test.js +55 -0
  28. package/dist/esm/ToolResponse/index.d.ts +2 -0
  29. package/dist/esm/ToolResponse/index.js +2 -0
  30. package/dist/esm/index.d.ts +2 -0
  31. package/dist/esm/index.js +2 -0
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +2 -2
  34. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +70 -0
  35. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +135 -0
  36. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +9 -1
  37. package/patternfly-docs/patternfly-docs.config.js +1 -1
  38. package/src/Message/Message.test.tsx +22 -0
  39. package/src/Message/Message.tsx +5 -0
  40. package/src/SourcesCard/SourcesCard.scss +17 -0
  41. package/src/SourcesCard/SourcesCard.test.tsx +93 -0
  42. package/src/SourcesCard/SourcesCard.tsx +116 -80
  43. package/src/ToolResponse/ToolResponse.scss +36 -0
  44. package/src/ToolResponse/ToolResponse.test.tsx +78 -0
  45. package/src/ToolResponse/ToolResponse.tsx +95 -0
  46. package/src/ToolResponse/index.ts +3 -0
  47. package/src/index.ts +3 -0
  48. package/src/main.scss +1 -0
@@ -521,6 +521,20 @@ describe('Message', () => {
521
521
  } }));
522
522
  expect(screen.getAllByRole('img')[1]).toHaveAttribute('src', 'test.png');
523
523
  }));
524
+ it('should handle tool response correctly', () => __awaiter(void 0, void 0, void 0, function* () {
525
+ render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: "Hi", toolResponse: {
526
+ toggleContent: 'Tool response: Name',
527
+ subheading: 'Thought for 3 seconds',
528
+ body: 'Lorem ipsum dolor sit amet',
529
+ cardTitle: 'Card title',
530
+ cardBody: 'Card body'
531
+ } }));
532
+ expect(screen.getByRole('button', { name: /Tool response: Name/i })).toBeTruthy();
533
+ expect(screen.getByText('Thought for 3 seconds')).toBeTruthy();
534
+ expect(screen.getByText('Lorem ipsum dolor sit amet')).toBeTruthy();
535
+ expect(screen.getByText('Card title')).toBeTruthy();
536
+ expect(screen.getByText('Card body')).toBeTruthy();
537
+ }));
524
538
  it('should handle block quote correctly', () => {
525
539
  render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: BLOCK_QUOTES }));
526
540
  expect(screen.getByText(/Blockquotes can also be nested.../)).toBeTruthy();
@@ -1,5 +1,5 @@
1
1
  import type { FunctionComponent } from 'react';
2
- import { ButtonProps, CardProps } from '@patternfly/react-core';
2
+ import { ButtonProps, CardBodyProps, CardFooterProps, CardProps, CardTitleProps, TruncateProps } from '@patternfly/react-core';
3
3
  export interface SourcesCardProps extends CardProps {
4
4
  /** Additional classes for the pagination navigation container. */
5
5
  className?: string;
@@ -13,6 +13,8 @@ export interface SourcesCardProps extends CardProps {
13
13
  sources: {
14
14
  /** Title of sources card */
15
15
  title?: string;
16
+ /** Subtitle of sources card */
17
+ subtitle?: string;
16
18
  /** Link to source */
17
19
  link: string;
18
20
  /** Body of sources card */
@@ -25,6 +27,10 @@ export interface SourcesCardProps extends CardProps {
25
27
  onClick?: React.MouseEventHandler<HTMLButtonElement>;
26
28
  /** Any additional props applied to the title of the Sources card */
27
29
  titleProps?: ButtonProps;
30
+ /** Custom footer applied to the Sources card */
31
+ footer?: React.ReactNode;
32
+ /** Additional props passed to Truncate component */
33
+ truncateProps?: TruncateProps;
28
34
  }[];
29
35
  /** Label for the English word "source" */
30
36
  sourceWord?: string;
@@ -44,6 +50,12 @@ export interface SourcesCardProps extends CardProps {
44
50
  showMoreWords?: string;
45
51
  /** Label for English words "show less" */
46
52
  showLessWords?: string;
53
+ /** Additional props passed to card title */
54
+ cardTitleProps?: CardTitleProps;
55
+ /** Additional props passed to card body */
56
+ cardBodyProps?: CardBodyProps;
57
+ /** Additional props passed to card footer */
58
+ cardFooterProps?: CardFooterProps;
47
59
  }
48
60
  declare const SourcesCard: FunctionComponent<SourcesCardProps>;
49
61
  export default SourcesCard;
@@ -16,7 +16,7 @@ import { Button, ButtonVariant, Card, CardBody, CardFooter, CardTitle, Expandabl
16
16
  import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
17
17
  const SourcesCard = (_a) => {
18
18
  var _b;
19
- var { className, isDisabled, paginationAriaLabel = 'Pagination', sources, sourceWord = 'source', sourceWordPlural = 'sources', toNextPageAriaLabel = 'Go to next page', toPreviousPageAriaLabel = 'Go to previous page', onNextClick, onPreviousClick, onSetPage, showMoreWords = 'show more', showLessWords = 'show less', isCompact } = _a, props = __rest(_a, ["className", "isDisabled", "paginationAriaLabel", "sources", "sourceWord", "sourceWordPlural", "toNextPageAriaLabel", "toPreviousPageAriaLabel", "onNextClick", "onPreviousClick", "onSetPage", "showMoreWords", "showLessWords", "isCompact"]);
19
+ var { className, isDisabled, paginationAriaLabel = 'Pagination', sources, sourceWord = 'source', sourceWordPlural = 'sources', toNextPageAriaLabel = 'Go to next page', toPreviousPageAriaLabel = 'Go to previous page', onNextClick, onPreviousClick, onSetPage, showMoreWords = 'show more', showLessWords = 'show less', isCompact, cardTitleProps, cardBodyProps, cardFooterProps } = _a, props = __rest(_a, ["className", "isDisabled", "paginationAriaLabel", "sources", "sourceWord", "sourceWordPlural", "toNextPageAriaLabel", "toPreviousPageAriaLabel", "onNextClick", "onPreviousClick", "onSetPage", "showMoreWords", "showLessWords", "isCompact", "cardTitleProps", "cardBodyProps", "cardFooterProps"]);
20
20
  const [page, setPage] = useState(1);
21
21
  const [isExpanded, setIsExpanded] = useState(false);
22
22
  const onToggle = (_event, isExpanded) => {
@@ -26,15 +26,15 @@ const SourcesCard = (_a) => {
26
26
  setPage(newPage);
27
27
  onSetPage && onSetPage(_evt, newPage);
28
28
  };
29
- const renderTitle = (title) => {
29
+ const renderTitle = (title, truncateProps) => {
30
30
  if (title) {
31
- return _jsx(Truncate, { content: title });
31
+ return _jsx(Truncate, Object.assign({ content: title }, truncateProps));
32
32
  }
33
33
  return `Source ${page}`;
34
34
  };
35
- return (_jsxs("div", { className: "pf-chatbot__source", children: [_jsx("span", { children: pluralize(sources.length, sourceWord, sourceWordPlural) }), _jsxs(Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [_jsx(CardTitle, { className: "pf-chatbot__sources-card-title", children: _jsx(Button, Object.assign({ component: "a", variant: ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? _jsx(ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, onClick: (_b = sources[page - 1].onClick) !== null && _b !== void 0 ? _b : undefined }, sources[page - 1].titleProps, { children: renderTitle(sources[page - 1].title) })) }), sources[page - 1].body && (_jsx(CardBody, { className: `pf-chatbot__sources-card-body`, children: sources[page - 1].hasShowMore ? (
35
+ return (_jsxs("div", { className: "pf-chatbot__source", children: [_jsx("span", { children: pluralize(sources.length, sourceWord, sourceWordPlural) }), _jsxs(Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [_jsx(CardTitle, Object.assign({ className: "pf-chatbot__sources-card-title" }, cardTitleProps, { children: _jsxs("div", { className: "pf-chatbot__sources-card-title-container", children: [_jsx(Button, Object.assign({ component: "a", variant: ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? _jsx(ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, onClick: (_b = sources[page - 1].onClick) !== null && _b !== void 0 ? _b : undefined }, sources[page - 1].titleProps, { children: renderTitle(sources[page - 1].title, sources[page - 1].truncateProps) })), sources[page - 1].subtitle && (_jsx("span", { className: "pf-chatbot__sources-card-subtitle", children: sources[page - 1].subtitle }))] }) })), sources[page - 1].body && (_jsx(CardBody, Object.assign({ className: `pf-chatbot__sources-card-body ${sources[page - 1].footer ? 'pf-chatbot__compact-sources-card-body' : undefined}` }, cardBodyProps, { children: sources[page - 1].hasShowMore ? (
36
36
  // prevents extra VO announcements of button text - parent Message has aria-live
37
- _jsx("div", { "aria-live": "off", children: _jsx(ExpandableSection, { variant: ExpandableSectionVariant.truncate, toggleText: isExpanded ? showLessWords : showMoreWords, onToggle: onToggle, isExpanded: isExpanded, truncateMaxLines: 2, children: sources[page - 1].body }) })) : (_jsx("div", { className: "pf-chatbot__sources-card-body-text", children: sources[page - 1].body })) })), sources.length > 1 && (_jsx(CardFooter, { className: "pf-chatbot__sources-card-footer-container", children: _jsx("div", { className: "pf-chatbot__sources-card-footer", children: _jsxs("nav", { className: `pf-chatbot__sources-card-footer-buttons ${className}`, "aria-label": paginationAriaLabel, children: [_jsx(Button, { variant: ButtonVariant.plain, isDisabled: isDisabled || page === 1, "data-action": "previous", onClick: (event) => {
37
+ _jsx("div", { "aria-live": "off", children: _jsx(ExpandableSection, { variant: ExpandableSectionVariant.truncate, toggleText: isExpanded ? showLessWords : showMoreWords, onToggle: onToggle, isExpanded: isExpanded, truncateMaxLines: 2, children: sources[page - 1].body }) })) : (_jsx("div", { className: "pf-chatbot__sources-card-body-text", children: sources[page - 1].body })) }))), sources[page - 1].footer ? (_jsx(CardFooter, Object.assign({ className: "pf-chatbot__sources-card-footer" }, cardFooterProps, { children: sources[page - 1].footer }))) : (sources.length > 1 && (_jsx(CardFooter, Object.assign({ className: "pf-chatbot__sources-card-footer-container" }, cardFooterProps, { children: _jsx("div", { className: "pf-chatbot__sources-card-footer", children: _jsxs("nav", { className: `pf-chatbot__sources-card-footer-buttons ${className}`, "aria-label": paginationAriaLabel, children: [_jsx(Button, { variant: ButtonVariant.plain, isDisabled: isDisabled || page === 1, "data-action": "previous", onClick: (event) => {
38
38
  const newPage = page >= 1 ? page - 1 : 1;
39
39
  onPreviousClick && onPreviousClick(event, newPage);
40
40
  handleNewPage(event, newPage);
@@ -42,6 +42,6 @@ const SourcesCard = (_a) => {
42
42
  const newPage = page + 1 <= sources.length ? page + 1 : sources.length;
43
43
  onNextClick && onNextClick(event, newPage);
44
44
  handleNewPage(event, newPage);
45
- }, children: _jsx(Icon, { isInline: true, iconSize: "lg", children: _jsx("svg", { className: "pf-v6-svg", viewBox: "0 0 180 500", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: _jsx("path", { d: "M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z" }) }) }) })] }) }) }))] }))] }));
45
+ }, children: _jsx(Icon, { isInline: true, iconSize: "lg", children: _jsx("svg", { className: "pf-v6-svg", viewBox: "0 0 180 500", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: _jsx("path", { d: "M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z" }) }) }) })] }) }) }))))] }))] }));
46
46
  };
47
47
  export default SourcesCard;
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { jsx as _jsx } from "react/jsx-runtime";
10
+ import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
11
11
  import { render, screen } from '@testing-library/react';
12
12
  import userEvent from '@testing-library/user-event';
13
13
  import '@testing-library/jest-dom';
@@ -179,4 +179,53 @@ describe('SourcesCard', () => {
179
179
  render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', titleProps: { className: 'test' } }] }));
180
180
  expect(screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test');
181
181
  });
182
+ it('should apply cardTitleProps appropriately', () => {
183
+ render(_jsx(SourcesCard, { cardTitleProps: { 'data-testid': 'card-title', className: 'test' }, sources: [{ title: 'How to make an apple pie', link: '' }] }));
184
+ expect(screen.getByTestId('card-title')).toHaveClass('test');
185
+ });
186
+ it('should apply cardBodyProps appropriately', () => {
187
+ render(_jsx(SourcesCard, { cardBodyProps: { 'data-testid': 'card-body', body: 'To make an apple pie, you must first...', className: 'test' }, sources: [{ title: 'How to make an apple pie', link: '', body: 'To make an apple pie, you must first...' }] }));
188
+ expect(screen.getByTestId('card-body')).toHaveClass('test');
189
+ });
190
+ it('should apply cardFooterProps appropriately', () => {
191
+ render(_jsx(SourcesCard, { cardFooterProps: { 'data-testid': 'card-footer', className: 'test' }, sources: [
192
+ { title: 'How to make an apple pie', link: '' },
193
+ { title: 'How to make cookies', link: '' }
194
+ ] }));
195
+ expect(screen.getByTestId('card-footer')).toHaveClass('test');
196
+ });
197
+ it('should apply truncateProps appropriately', () => {
198
+ render(_jsx(SourcesCard, { sources: [
199
+ {
200
+ title: 'How to make an apple pie',
201
+ link: '',
202
+ truncateProps: { 'data-testid': 'card-truncate', className: 'test' }
203
+ }
204
+ ] }));
205
+ expect(screen.getByTestId('card-truncate')).toHaveClass('test');
206
+ });
207
+ it('should apply custom footer appropriately when there is one source', () => {
208
+ render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', footer: _jsx(_Fragment, { children: "I am a custom footer" }) }] }));
209
+ expect(screen.getByText('I am a custom footer'));
210
+ expect(screen.queryByText('1/1')).toBeFalsy();
211
+ });
212
+ it('should apply custom footer appropriately when are multiple sources', () => {
213
+ render(_jsx(SourcesCard, { sources: [
214
+ { title: 'How to make an apple pie', link: '', footer: _jsx(_Fragment, { children: "I am a custom footer" }) },
215
+ { title: 'How to bake bread', link: '' }
216
+ ] }));
217
+ expect(screen.getByText('I am a custom footer'));
218
+ // does not show navigation bar
219
+ expect(screen.queryByText('1/2')).toBeFalsy();
220
+ });
221
+ it('should apply footer props to custom footer appropriately', () => {
222
+ render(_jsx(SourcesCard, { cardFooterProps: { 'data-testid': 'card-footer', className: 'test' }, sources: [{ title: 'How to make an apple pie', link: '', footer: _jsx(_Fragment, { children: "I am a custom footer" }) }] }));
223
+ expect(screen.getByText('I am a custom footer'));
224
+ expect(screen.getByTestId('card-footer')).toHaveClass('test');
225
+ });
226
+ it('should apply subtitle appropriately', () => {
227
+ render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', subtitle: 'You must first create the universe' }] }));
228
+ expect(screen.getByText('How to make an apple pie'));
229
+ expect(screen.getByText('You must first create the universe'));
230
+ });
182
231
  });
@@ -0,0 +1,30 @@
1
+ import { CardBodyProps, CardProps, CardTitleProps, DividerProps, ExpandableSectionProps } from '@patternfly/react-core';
2
+ import { type FunctionComponent } from 'react';
3
+ export interface ToolResponseProps {
4
+ /** Toggle content shown for expandable section */
5
+ toggleContent: React.ReactNode;
6
+ /** Additional props passed to expandable section */
7
+ expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
8
+ /** Subheading rendered inside expandable section */
9
+ subheading?: string;
10
+ /** Body text rendered inside expandable section */
11
+ body?: React.ReactNode | string;
12
+ /** Content passed into tool response card body */
13
+ cardBody: React.ReactNode;
14
+ /** Content passed into tool response card title */
15
+ cardTitle: React.ReactNode;
16
+ /** Additional props passed to main card */
17
+ cardProps?: CardProps;
18
+ /** Additional props passed to main card body */
19
+ cardBodyProps?: CardBodyProps;
20
+ /** Additional props passed to tool response card */
21
+ toolResponseCardProps?: CardProps;
22
+ /** Additional props passed to tool response card body */
23
+ toolResponseCardBodyProps?: CardBodyProps;
24
+ /** Additional props passed to tool response card divider */
25
+ toolResponseCardDividerProps?: DividerProps;
26
+ /** Additional props passed to tool response card title */
27
+ toolResponseCardTitleProps?: CardTitleProps;
28
+ }
29
+ export declare const ToolResponse: FunctionComponent<ToolResponseProps>;
30
+ export default ToolResponse;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // ============================================================================
3
+ // Tool Response Card
4
+ // ============================================================================
5
+ import { Card, CardBody, CardTitle, Divider, ExpandableSection } from '@patternfly/react-core';
6
+ import { useState } from 'react';
7
+ export const ToolResponse = ({ body, cardProps, expandableSectionProps, subheading, cardBody, cardTitle, cardBodyProps, toggleContent, toolResponseCardBodyProps, toolResponseCardDividerProps, toolResponseCardProps, toolResponseCardTitleProps }) => {
8
+ const [isExpanded, setIsExpanded] = useState(true);
9
+ const onToggle = (_event, isExpanded) => {
10
+ setIsExpanded(isExpanded);
11
+ };
12
+ return (_jsx(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-response" }, cardProps, { children: _jsx(CardBody, Object.assign({}, cardBodyProps, { children: _jsx(ExpandableSection, Object.assign({ toggleContent: toggleContent, onToggle: onToggle, isExpanded: isExpanded, isIndented: true, className: "pf-chatbot__tool-response-expandable-section" }, expandableSectionProps, { children: _jsxs("div", { className: "pf-chatbot__tool-response-section", children: [subheading && (_jsx("div", { className: "pf-chatbot__tool-response-subheading", children: _jsx("span", { children: subheading }) })), body && _jsx("div", { className: "pf-chatbot__tool-response-body", children: body }), _jsxs(Card, Object.assign({ isCompact: true, className: "pf-chatbot__tool-response-card" }, toolResponseCardProps, { children: [_jsx(CardTitle, Object.assign({}, toolResponseCardTitleProps, { children: cardTitle })), _jsx(Divider, Object.assign({}, toolResponseCardDividerProps)), _jsx(CardBody, Object.assign({}, toolResponseCardBodyProps, { children: cardBody }))] }))] }) })) })) })));
13
+ };
14
+ export default ToolResponse;
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { render, screen } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import ToolResponse from './ToolResponse';
5
+ describe('ToolResponse', () => {
6
+ const defaultProps = {
7
+ toggleContent: 'Tool response: toolName',
8
+ cardTitle: 'Title',
9
+ cardBody: 'Body'
10
+ };
11
+ it('should render with required props only', () => {
12
+ render(_jsx(ToolResponse, Object.assign({}, defaultProps)));
13
+ expect(screen.getByText('Title')).toBeTruthy();
14
+ expect(screen.getByText('Body')).toBeTruthy();
15
+ expect(screen.getByText('Tool response: toolName')).toBeTruthy();
16
+ });
17
+ it('should render subheading when provided', () => {
18
+ const subheading = 'Tool execution result';
19
+ render(_jsx(ToolResponse, Object.assign({}, defaultProps, { subheading: subheading })));
20
+ expect(screen.getByText(subheading)).toBeTruthy();
21
+ });
22
+ it('should render body content when provided', () => {
23
+ const body = 'This is the tool response body content';
24
+ render(_jsx(ToolResponse, Object.assign({}, defaultProps, { body: body })));
25
+ expect(screen.getByText(body)).toBeTruthy();
26
+ });
27
+ it('should render with complex content including React elements', () => {
28
+ const body = (_jsxs("div", { children: [_jsx("p", { children: "Complex body content" }), _jsxs("ul", { children: [_jsx("li", { children: "Item 1" }), _jsx("li", { children: "Item 2" })] })] }));
29
+ const cardTitle = _jsx("strong", { children: "API Response" });
30
+ const cardBody = (_jsx("div", { children: _jsx("code", { children: "{ status: 'success' }" }) }));
31
+ render(_jsx(ToolResponse, Object.assign({}, defaultProps, { body: body, cardTitle: cardTitle, cardBody: cardBody })));
32
+ expect(screen.getByText('Complex body content')).toBeTruthy();
33
+ expect(screen.getByText('Item 1')).toBeTruthy();
34
+ expect(screen.getByText('Item 2')).toBeTruthy();
35
+ expect(screen.getByText('API Response')).toBeTruthy();
36
+ expect(screen.getByText("{ status: 'success' }")).toBeTruthy();
37
+ });
38
+ it('should apply custom className from cardProps', () => {
39
+ const { container } = render(_jsx(ToolResponse, Object.assign({}, defaultProps, { cardProps: { className: 'custom-tool-response-class' } })));
40
+ expect(container.querySelector('.custom-tool-response-class')).toBeTruthy();
41
+ });
42
+ it('should pass through expandableSectionProps', () => {
43
+ render(_jsx(ToolResponse, Object.assign({}, defaultProps, { expandableSectionProps: { className: 'custom-expandable-class' } })));
44
+ expect(document.querySelector('.custom-expandable-class')).toBeTruthy();
45
+ });
46
+ it('should pass through toolResponseCardProps', () => {
47
+ render(_jsx(ToolResponse, Object.assign({}, defaultProps, { toolResponseCardProps: { className: 'custom-card-class' } })));
48
+ expect(document.querySelector('.custom-card-class')).toBeTruthy();
49
+ });
50
+ it('should not render subheading span when subheading is not provided', () => {
51
+ const { container } = render(_jsx(ToolResponse, Object.assign({}, defaultProps)));
52
+ const subheadingContainer = container.querySelector('.pf-chatbot__tool-response-subheading');
53
+ expect(subheadingContainer).toBeFalsy();
54
+ });
55
+ });
@@ -0,0 +1,2 @@
1
+ export { default } from './ToolResponse';
2
+ export * from './ToolResponse';
@@ -0,0 +1,2 @@
1
+ export { default } from './ToolResponse';
2
+ export * from './ToolResponse';
@@ -54,5 +54,7 @@ export { default as SourcesCard } from './SourcesCard';
54
54
  export * from './SourcesCard';
55
55
  export { default as TermsOfUse } from './TermsOfUse';
56
56
  export * from './TermsOfUse';
57
+ export { default as ToolResponse } from './ToolResponse';
58
+ export * from './ToolResponse';
57
59
  export { default as tracking } from './tracking';
58
60
  export * from './tracking';
package/dist/esm/index.js CHANGED
@@ -55,5 +55,7 @@ export { default as SourcesCard } from './SourcesCard';
55
55
  export * from './SourcesCard';
56
56
  export { default as TermsOfUse } from './TermsOfUse';
57
57
  export * from './TermsOfUse';
58
+ export { default as ToolResponse } from './ToolResponse';
59
+ export * from './ToolResponse';
58
60
  export { default as tracking } from './tracking';
59
61
  export * from './tracking';
@@ -1 +1 @@
1
- {"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.test.tsx","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.test.tsx","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.test.tsx","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.test.tsx","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/EmptyState.tsx","../src/ChatbotConversationHistoryNav/LoadingState.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.test.tsx","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFooternote.test.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.test.tsx","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.test.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.test.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.test.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.test.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.test.tsx","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.test.tsx","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/Compare/Compare.test.tsx","../src/Compare/Compare.tsx","../src/Compare/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageInput.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/ErrorMessage/ErrorMessage.tsx","../src/Message/ImageMessage/ImageMessage.tsx","../src/Message/LinkMessage/LinkMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/Plugins/index.ts","../src/Message/Plugins/rehypeCodeBlockToggle.ts","../src/Message/Plugins/rehypeMoveImagesOutOfParagraphs.ts","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TableMessage/TableMessage.tsx","../src/Message/TableMessage/TbodyMessage.tsx","../src/Message/TableMessage/TdMessage.tsx","../src/Message/TableMessage/ThMessage.tsx","../src/Message/TableMessage/TheadMessage.tsx","../src/Message/TableMessage/TrMessage.tsx","../src/Message/TextMessage/TextMessage.tsx","../src/Message/UserFeedback/CloseButton.tsx","../src/Message/UserFeedback/UserFeedback.test.tsx","../src/Message/UserFeedback/UserFeedback.tsx","../src/Message/UserFeedback/UserFeedbackComplete.test.tsx","../src/Message/UserFeedback/UserFeedbackComplete.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.test.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/MessageDivider/MessageDivider.test.tsx","../src/MessageDivider/MessageDivider.tsx","../src/MessageDivider/index.ts","../src/PreviewAttachment/PreviewAttachment.test.tsx","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.test.tsx","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts","../src/__mocks__/rehype-external-links.ts","../src/__mocks__/rehype-sanitize.ts","../src/__mocks__/rehype-unwrap-images.tsx","../src/tracking/console_tracking_provider.ts","../src/tracking/index.ts","../src/tracking/posthog_tracking_provider.ts","../src/tracking/segment_tracking_provider.ts","../src/tracking/trackingProviderProxy.ts","../src/tracking/tracking_api.ts","../src/tracking/tracking_registry.ts","../src/tracking/tracking_spi.ts","../src/tracking/umami_tracking_provider.ts"],"version":"5.6.3"}
1
+ {"root":["../src/index.ts","../src/AttachMenu/AttachMenu.tsx","../src/AttachMenu/index.ts","../src/AttachmentEdit/AttachmentEdit.test.tsx","../src/AttachmentEdit/AttachmentEdit.tsx","../src/AttachmentEdit/index.ts","../src/Chatbot/Chatbot.test.tsx","../src/Chatbot/Chatbot.tsx","../src/Chatbot/index.ts","../src/ChatbotAlert/ChatbotAlert.test.tsx","../src/ChatbotAlert/ChatbotAlert.tsx","../src/ChatbotAlert/index.ts","../src/ChatbotContent/ChatbotContent.test.tsx","../src/ChatbotContent/ChatbotContent.tsx","../src/ChatbotContent/index.ts","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx","../src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx","../src/ChatbotConversationHistoryNav/EmptyState.tsx","../src/ChatbotConversationHistoryNav/LoadingState.tsx","../src/ChatbotConversationHistoryNav/index.ts","../src/ChatbotFooter/ChatbotFooter.test.tsx","../src/ChatbotFooter/ChatbotFooter.tsx","../src/ChatbotFooter/ChatbotFooternote.test.tsx","../src/ChatbotFooter/ChatbotFootnote.tsx","../src/ChatbotFooter/index.ts","../src/ChatbotHeader/ChatbotHeader.test.tsx","../src/ChatbotHeader/ChatbotHeader.tsx","../src/ChatbotHeader/ChatbotHeaderActions.test.tsx","../src/ChatbotHeader/ChatbotHeaderActions.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderCloseButton.tsx","../src/ChatbotHeader/ChatbotHeaderMain.test.tsx","../src/ChatbotHeader/ChatbotHeaderMain.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.test.tsx","../src/ChatbotHeader/ChatbotHeaderMenu.tsx","../src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx","../src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderOptionsDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx","../src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.test.tsx","../src/ChatbotHeader/ChatbotHeaderTitle.tsx","../src/ChatbotHeader/index.ts","../src/ChatbotModal/ChatbotModal.test.tsx","../src/ChatbotModal/ChatbotModal.tsx","../src/ChatbotModal/index.ts","../src/ChatbotPopover/ChatbotPopover.tsx","../src/ChatbotPopover/index.ts","../src/ChatbotToggle/ChatbotToggle.test.tsx","../src/ChatbotToggle/ChatbotToggle.tsx","../src/ChatbotToggle/index.ts","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx","../src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx","../src/ChatbotWelcomePrompt/index.ts","../src/CodeModal/CodeModal.test.tsx","../src/CodeModal/CodeModal.tsx","../src/CodeModal/index.ts","../src/Compare/Compare.test.tsx","../src/Compare/Compare.tsx","../src/Compare/index.ts","../src/FileDetails/FileDetails.test.tsx","../src/FileDetails/FileDetails.tsx","../src/FileDetails/index.ts","../src/FileDetailsLabel/FileDetailsLabel.test.tsx","../src/FileDetailsLabel/FileDetailsLabel.tsx","../src/FileDetailsLabel/index.ts","../src/FileDropZone/FileDropZone.test.tsx","../src/FileDropZone/FileDropZone.tsx","../src/FileDropZone/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/Message/Message.test.tsx","../src/Message/Message.tsx","../src/Message/MessageInput.tsx","../src/Message/MessageLoading.tsx","../src/Message/index.ts","../src/Message/CodeBlockMessage/CodeBlockMessage.tsx","../src/Message/ErrorMessage/ErrorMessage.tsx","../src/Message/ImageMessage/ImageMessage.tsx","../src/Message/LinkMessage/LinkMessage.tsx","../src/Message/ListMessage/ListItemMessage.tsx","../src/Message/ListMessage/OrderedListMessage.tsx","../src/Message/ListMessage/UnorderedListMessage.tsx","../src/Message/Plugins/index.ts","../src/Message/Plugins/rehypeCodeBlockToggle.ts","../src/Message/Plugins/rehypeMoveImagesOutOfParagraphs.ts","../src/Message/QuickResponse/QuickResponse.tsx","../src/Message/QuickStarts/FallbackImg.tsx","../src/Message/QuickStarts/QuickStartTile.tsx","../src/Message/QuickStarts/QuickStartTileDescription.test.tsx","../src/Message/QuickStarts/QuickStartTileDescription.tsx","../src/Message/QuickStarts/QuickStartTileHeader.tsx","../src/Message/QuickStarts/monitor-sampleapp-quickstart-with-image.ts","../src/Message/QuickStarts/monitor-sampleapp-quickstart.ts","../src/Message/QuickStarts/types.ts","../src/Message/TableMessage/TableMessage.tsx","../src/Message/TableMessage/TbodyMessage.tsx","../src/Message/TableMessage/TdMessage.tsx","../src/Message/TableMessage/ThMessage.tsx","../src/Message/TableMessage/TheadMessage.tsx","../src/Message/TableMessage/TrMessage.tsx","../src/Message/TextMessage/TextMessage.tsx","../src/Message/UserFeedback/CloseButton.tsx","../src/Message/UserFeedback/UserFeedback.test.tsx","../src/Message/UserFeedback/UserFeedback.tsx","../src/Message/UserFeedback/UserFeedbackComplete.test.tsx","../src/Message/UserFeedback/UserFeedbackComplete.tsx","../src/MessageBar/AttachButton.test.tsx","../src/MessageBar/AttachButton.tsx","../src/MessageBar/MessageBar.test.tsx","../src/MessageBar/MessageBar.tsx","../src/MessageBar/MicrophoneButton.tsx","../src/MessageBar/SendButton.test.tsx","../src/MessageBar/SendButton.tsx","../src/MessageBar/StopButton.test.tsx","../src/MessageBar/StopButton.tsx","../src/MessageBar/index.ts","../src/MessageBox/JumpButton.test.tsx","../src/MessageBox/JumpButton.tsx","../src/MessageBox/MessageBox.test.tsx","../src/MessageBox/MessageBox.tsx","../src/MessageBox/index.ts","../src/MessageDivider/MessageDivider.test.tsx","../src/MessageDivider/MessageDivider.tsx","../src/MessageDivider/index.ts","../src/PreviewAttachment/PreviewAttachment.test.tsx","../src/PreviewAttachment/PreviewAttachment.tsx","../src/PreviewAttachment/index.ts","../src/ResponseActions/ResponseActionButton.test.tsx","../src/ResponseActions/ResponseActionButton.tsx","../src/ResponseActions/ResponseActions.test.tsx","../src/ResponseActions/ResponseActions.tsx","../src/ResponseActions/index.ts","../src/Settings/SettingsForm.test.tsx","../src/Settings/SettingsForm.tsx","../src/Settings/index.ts","../src/SourceDetailsMenuItem/SourceDetailsMenuItem.tsx","../src/SourceDetailsMenuItem/index.ts","../src/SourcesCard/SourcesCard.test.tsx","../src/SourcesCard/SourcesCard.tsx","../src/SourcesCard/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts","../src/ToolResponse/ToolResponse.test.tsx","../src/ToolResponse/ToolResponse.tsx","../src/ToolResponse/index.ts","../src/__mocks__/rehype-external-links.ts","../src/__mocks__/rehype-sanitize.ts","../src/__mocks__/rehype-unwrap-images.tsx","../src/tracking/console_tracking_provider.ts","../src/tracking/index.ts","../src/tracking/posthog_tracking_provider.ts","../src/tracking/segment_tracking_provider.ts","../src/tracking/trackingProviderProxy.ts","../src/tracking/tracking_api.ts","../src/tracking/tracking_registry.ts","../src/tracking/tracking_spi.ts","../src/tracking/umami_tracking_provider.ts"],"version":"5.6.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/chatbot",
3
- "version": "6.4.0-prerelease.15",
3
+ "version": "6.4.0-prerelease.17",
4
4
  "description": "This library provides React components based on PatternFly 6 that can be used to build chatbots.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -53,7 +53,7 @@
53
53
  "react-dom": "^18 || ^19"
54
54
  },
55
55
  "devDependencies": {
56
- "@patternfly/documentation-framework": "6.16.0",
56
+ "@patternfly/documentation-framework": "6.19.0",
57
57
  "@patternfly/patternfly": "^6.1.0",
58
58
  "@patternfly/patternfly-a11y": "^5.0.0",
59
59
  "@types/dom-speech-recognition": "^0.0.4",
@@ -1,6 +1,8 @@
1
1
  import { FunctionComponent, MouseEvent as ReactMouseEvent, KeyboardEvent as ReactKeyboardEvent } from 'react';
2
2
  import Message from '@patternfly/chatbot/dist/dynamic/Message';
3
3
  import patternflyAvatar from './patternfly_avatar.jpg';
4
+ import { Button, Flex, FlexItem, Label, Popover } from '@patternfly/react-core';
5
+ import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
4
6
 
5
7
  export const MessageWithSourcesExample: FunctionComponent = () => {
6
8
  const onSetPage = (_event: ReactMouseEvent | ReactKeyboardEvent | MouseEvent, newPage: number) => {
@@ -8,8 +10,76 @@ export const MessageWithSourcesExample: FunctionComponent = () => {
8
10
  console.log(`Page changed to ${newPage}`);
9
11
  };
10
12
 
13
+ const date = new Date();
14
+
15
+ const datePart = date.toLocaleDateString('en', {
16
+ year: 'numeric',
17
+ month: 'short',
18
+ day: 'numeric'
19
+ });
20
+
21
+ const timePart = date.toLocaleTimeString('en', {
22
+ hour: '2-digit',
23
+ minute: '2-digit',
24
+ hour12: true
25
+ });
26
+
27
+ const formattedDate = `${datePart}, ${timePart}`;
28
+
11
29
  return (
12
30
  <>
31
+ <Message
32
+ name="Bot"
33
+ role="bot"
34
+ avatar={patternflyAvatar}
35
+ content="This example has a custom subtitle and footer with no pagination"
36
+ sources={{
37
+ sources: [
38
+ {
39
+ title: 'Getting started with Red Hat OpenShift',
40
+ subtitle: 'Red Hat knowledge base',
41
+ link: '#',
42
+ body: 'Red Hat OpenShift on IBM Cloud is a managed offering to create your own cluster of compute hosts where you can deploy and manage containerized apps on IBM Cloud ...',
43
+ isExternal: true,
44
+ footer: (
45
+ <Flex className="pf-chatbot__sources-card-subtle" gap={{ default: 'gapXs' }}>
46
+ <FlexItem alignSelf={{ default: 'alignSelfStretch' }}>
47
+ <Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
48
+ <FlexItem>
49
+ <Label color="green">Confidence 93%</Label>
50
+ </FlexItem>
51
+ <FlexItem>
52
+ <Popover
53
+ headerContent={
54
+ <Flex gap={{ default: 'gapXs' }}>
55
+ <FlexItem>
56
+ <OutlinedQuestionCircleIcon />
57
+ </FlexItem>
58
+ <FlexItem>Why this confidence score?</FlexItem>
59
+ </Flex>
60
+ }
61
+ bodyContent={
62
+ <>
63
+ A high confidence score indicates a strong match. The system found significant overlap in
64
+ key data points, including text content, names, dates, and organizational details, with a
65
+ high degree of certainty. This match is highly reliable.
66
+ </>
67
+ }
68
+ >
69
+ <Button variant="link" icon={<OutlinedQuestionCircleIcon />}>
70
+ Learn about this score
71
+ </Button>
72
+ </Popover>
73
+ </FlexItem>
74
+ </Flex>
75
+ </FlexItem>
76
+ <FlexItem>{`Last updated: ${formattedDate}`}</FlexItem>
77
+ </Flex>
78
+ )
79
+ }
80
+ ]
81
+ }}
82
+ />
13
83
  <Message
14
84
  name="Bot"
15
85
  role="bot"
@@ -0,0 +1,135 @@
1
+ import { useState, FunctionComponent, MouseEvent as ReactMouseEvent } from 'react';
2
+ import Message from '@patternfly/chatbot/dist/dynamic/Message';
3
+ import patternflyAvatar from './patternfly_avatar.jpg';
4
+ import { CopyIcon, WrenchIcon } from '@patternfly/react-icons';
5
+ import {
6
+ Button,
7
+ DescriptionList,
8
+ DescriptionListDescription,
9
+ DescriptionListGroup,
10
+ DescriptionListTerm,
11
+ ExpandableSection,
12
+ ExpandableSectionVariant,
13
+ Flex,
14
+ FlexItem,
15
+ Label
16
+ } from '@patternfly/react-core';
17
+
18
+ export const MessageWithToolResponseExample: FunctionComponent = () => {
19
+ const [isExpanded, setIsExpanded] = useState(false);
20
+
21
+ const onToggle = (_event: ReactMouseEvent, isExpanded: boolean) => {
22
+ setIsExpanded(isExpanded);
23
+ };
24
+
25
+ return (
26
+ <Message
27
+ name="Bot"
28
+ role="bot"
29
+ avatar={patternflyAvatar}
30
+ content="This example has a body description that's within the recommended limit of 2 lines:"
31
+ toolResponse={{
32
+ toggleContent: 'Tool response: toolName',
33
+ subheading: 'Thought for 3 seconds',
34
+ body: "Here's the summary for your toolName response:",
35
+ cardTitle: (
36
+ <Flex alignItems={{ default: 'alignItemsCenter' }} justifyContent={{ default: 'justifyContentSpaceBetween' }}>
37
+ <FlexItem>
38
+ <Flex direction={{ default: 'column' }} gap={{ default: 'gapXs' }}>
39
+ <FlexItem grow={{ default: 'grow' }}>
40
+ <Flex gap={{ default: 'gapXs' }}>
41
+ <FlexItem>
42
+ <WrenchIcon style={{ color: 'var(--pf-t--global--icon--color--brand--default' }} />
43
+ </FlexItem>
44
+ <FlexItem>toolName</FlexItem>
45
+ </Flex>
46
+ </FlexItem>
47
+ <FlexItem>
48
+ <Flex gap={{ default: 'gapSm' }} style={{ fontSize: '12px', fontWeight: '400' }}>
49
+ <FlexItem>Execution time:</FlexItem>
50
+ <FlexItem>0.12 seconds</FlexItem>
51
+ </Flex>
52
+ </FlexItem>
53
+ </Flex>
54
+ </FlexItem>
55
+ <FlexItem>
56
+ <Button
57
+ variant="plain"
58
+ aria-label="Copy tool response to clipboard"
59
+ icon={<CopyIcon style={{ color: 'var(--pf-t--global--icon--color--subtle)' }} />}
60
+ ></Button>
61
+ </FlexItem>
62
+ </Flex>
63
+ ),
64
+ cardBody: (
65
+ <>
66
+ <DescriptionList
67
+ style={{ '--pf-v6-c-description-list--RowGap': 'var(--pf-t--global--spacer--md)' } as any}
68
+ aria-label="Tool response"
69
+ >
70
+ <DescriptionListGroup
71
+ style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
72
+ >
73
+ <DescriptionListTerm>Parameters</DescriptionListTerm>
74
+ <DescriptionListDescription>
75
+ <Flex direction={{ default: 'column' }}>
76
+ <FlexItem>Optional description text for parameters.</FlexItem>
77
+ <FlexItem>
78
+ <Flex gap={{ default: 'gapSm' }}>
79
+ <FlexItem>
80
+ <Label variant="outline" color="blue">
81
+ type
82
+ </Label>
83
+ </FlexItem>
84
+ <FlexItem>
85
+ <Label variant="outline" color="blue">
86
+ properties
87
+ </Label>
88
+ </FlexItem>
89
+ <FlexItem>
90
+ <Label variant="outline" color="blue">
91
+ label
92
+ </Label>
93
+ </FlexItem>
94
+ <FlexItem>
95
+ <Label variant="outline" color="blue">
96
+ label
97
+ </Label>
98
+ </FlexItem>
99
+ </Flex>
100
+ </FlexItem>
101
+ </Flex>
102
+ </DescriptionListDescription>
103
+ </DescriptionListGroup>
104
+ <DescriptionListGroup
105
+ style={{ '--pf-v6-c-description-list__group--RowGap': 'var(--pf-t--global--spacer--xs)' } as any}
106
+ >
107
+ <DescriptionListTerm>Response</DescriptionListTerm>
108
+ <DescriptionListDescription>
109
+ <ExpandableSection
110
+ variant={ExpandableSectionVariant.truncate}
111
+ toggleTextExpanded="show less of response"
112
+ toggleTextCollapsed="show more of response"
113
+ onToggle={onToggle}
114
+ isExpanded={isExpanded}
115
+ style={
116
+ {
117
+ '--pf-v6-c-expandable-section__content--Opacity': '1',
118
+ '--pf-v6-c-expandable-section__content--PaddingInlineStart': 0,
119
+ '--pf-v6-c-expandable-section__content--TranslateY': 0,
120
+ '--pf-v6-c-expandable-section--m-expand-top__content--TranslateY': 0
121
+ } as any
122
+ }
123
+ >
124
+ Descriptive text about the tool response, including completion status, details on the data that was
125
+ processed, or anything else relevant to the use case.
126
+ </ExpandableSection>
127
+ </DescriptionListDescription>
128
+ </DescriptionListGroup>
129
+ </DescriptionList>
130
+ </>
131
+ )
132
+ }}
133
+ />
134
+ );
135
+ };
@@ -34,7 +34,7 @@ import Message from '@patternfly/chatbot/dist/dynamic/Message';
34
34
  import MessageDivider from '@patternfly/chatbot/dist/dynamic/MessageDivider';
35
35
  import { rehypeCodeBlockToggle } from '@patternfly/chatbot/dist/esm/Message/Plugins/rehypeCodeBlockToggle';
36
36
  import SourcesCard from '@patternfly/chatbot/dist/dynamic/SourcesCard';
37
- import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, RedoIcon, RobotIcon } from '@patternfly/react-icons';
37
+ import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CopyIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, OutlinedQuestionCircleIcon, RedoIcon, RobotIcon, WrenchIcon } from '@patternfly/react-icons';
38
38
  import patternflyAvatar from './patternfly_avatar.jpg';
39
39
  import AttachmentEdit from '@patternfly/chatbot/dist/dynamic/AttachmentEdit';
40
40
  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
178
178
 
179
179
  ```
180
180
 
181
+ ### Messages with tool responses
182
+
183
+ 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 `<Message>` 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.
184
+
185
+ ```js file="./MessageWithToolResponse.tsx"
186
+
187
+ ```
188
+
181
189
  ### Messages with quick start tiles
182
190
 
183
191
  [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.
@@ -2,7 +2,7 @@
2
2
  module.exports = {
3
3
  sideNavItems: [{ section: 'PatternFly-AI' }],
4
4
  topNavItems: [],
5
- hasDarkThemeSwitcher: true,
5
+ hasThemeSwitcher: true,
6
6
  hasRTLSwitcher: true,
7
7
  port: 8006
8
8
  };