@patternfly/chatbot 6.5.0-prerelease.21 → 6.5.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.
Files changed (97) hide show
  1. package/dist/cjs/DeepThinking/DeepThinking.d.ts +13 -0
  2. package/dist/cjs/DeepThinking/DeepThinking.js +31 -3
  3. package/dist/cjs/DeepThinking/DeepThinking.test.js +80 -0
  4. package/dist/cjs/MarkdownContent/MarkdownContent.d.ts +39 -0
  5. package/dist/cjs/MarkdownContent/MarkdownContent.js +181 -0
  6. package/dist/cjs/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  7. package/dist/cjs/MarkdownContent/MarkdownContent.test.js +192 -0
  8. package/dist/cjs/MarkdownContent/index.d.ts +2 -0
  9. package/dist/cjs/MarkdownContent/index.js +23 -0
  10. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  11. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
  12. package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +5 -1
  13. package/dist/cjs/Message/LinkMessage/LinkMessage.js +4 -3
  14. package/dist/cjs/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  15. package/dist/cjs/Message/ListMessage/OrderedListMessage.js +2 -1
  16. package/dist/cjs/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  17. package/dist/cjs/Message/ListMessage/UnorderedListMessage.js +2 -1
  18. package/dist/cjs/Message/Message.js +2 -155
  19. package/dist/cjs/Message/TableMessage/TableMessage.d.ts +6 -1
  20. package/dist/cjs/Message/TableMessage/TableMessage.js +3 -2
  21. package/dist/cjs/Message/TextMessage/TextMessage.d.ts +8 -1
  22. package/dist/cjs/Message/TextMessage/TextMessage.js +3 -2
  23. package/dist/cjs/ToolCall/ToolCall.d.ts +11 -0
  24. package/dist/cjs/ToolCall/ToolCall.js +24 -3
  25. package/dist/cjs/ToolCall/ToolCall.test.js +57 -0
  26. package/dist/cjs/ToolResponse/ToolResponse.d.ts +17 -0
  27. package/dist/cjs/ToolResponse/ToolResponse.js +49 -3
  28. package/dist/cjs/ToolResponse/ToolResponse.test.js +100 -0
  29. package/dist/cjs/index.d.ts +2 -0
  30. package/dist/cjs/index.js +4 -1
  31. package/dist/css/main.css +48 -0
  32. package/dist/css/main.css.map +1 -1
  33. package/dist/dynamic/MarkdownContent/package.json +1 -0
  34. package/dist/esm/DeepThinking/DeepThinking.d.ts +13 -0
  35. package/dist/esm/DeepThinking/DeepThinking.js +28 -3
  36. package/dist/esm/DeepThinking/DeepThinking.test.js +80 -0
  37. package/dist/esm/MarkdownContent/MarkdownContent.d.ts +39 -0
  38. package/dist/esm/MarkdownContent/MarkdownContent.js +174 -0
  39. package/dist/esm/MarkdownContent/MarkdownContent.test.d.ts +1 -0
  40. package/dist/esm/MarkdownContent/MarkdownContent.test.js +187 -0
  41. package/dist/esm/MarkdownContent/index.d.ts +2 -0
  42. package/dist/esm/MarkdownContent/index.js +2 -0
  43. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.d.ts +3 -1
  44. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +3 -2
  45. package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +5 -1
  46. package/dist/esm/Message/LinkMessage/LinkMessage.js +4 -3
  47. package/dist/esm/Message/ListMessage/OrderedListMessage.d.ts +9 -1
  48. package/dist/esm/Message/ListMessage/OrderedListMessage.js +2 -1
  49. package/dist/esm/Message/ListMessage/UnorderedListMessage.d.ts +7 -1
  50. package/dist/esm/Message/ListMessage/UnorderedListMessage.js +2 -1
  51. package/dist/esm/Message/Message.js +3 -156
  52. package/dist/esm/Message/TableMessage/TableMessage.d.ts +6 -1
  53. package/dist/esm/Message/TableMessage/TableMessage.js +3 -2
  54. package/dist/esm/Message/TextMessage/TextMessage.d.ts +8 -1
  55. package/dist/esm/Message/TextMessage/TextMessage.js +3 -2
  56. package/dist/esm/ToolCall/ToolCall.d.ts +11 -0
  57. package/dist/esm/ToolCall/ToolCall.js +21 -3
  58. package/dist/esm/ToolCall/ToolCall.test.js +57 -0
  59. package/dist/esm/ToolResponse/ToolResponse.d.ts +17 -0
  60. package/dist/esm/ToolResponse/ToolResponse.js +46 -3
  61. package/dist/esm/ToolResponse/ToolResponse.test.js +100 -0
  62. package/dist/esm/index.d.ts +2 -0
  63. package/dist/esm/index.js +2 -0
  64. package/dist/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +1 -1
  66. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +25 -11
  67. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownDeepThinking.tsx +26 -0
  68. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolCall.tsx +29 -0
  69. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolResponse.tsx +200 -0
  70. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolCall.tsx +14 -1
  71. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +222 -105
  72. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +32 -0
  73. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +15 -1
  74. package/src/DeepThinking/DeepThinking.test.tsx +109 -0
  75. package/src/DeepThinking/DeepThinking.tsx +54 -5
  76. package/src/MarkdownContent/MarkdownContent.test.tsx +207 -0
  77. package/src/MarkdownContent/MarkdownContent.tsx +264 -0
  78. package/src/MarkdownContent/index.ts +2 -0
  79. package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +4 -0
  80. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +5 -1
  81. package/src/Message/LinkMessage/LinkMessage.scss +5 -0
  82. package/src/Message/LinkMessage/LinkMessage.tsx +24 -2
  83. package/src/Message/ListMessage/ListMessage.scss +8 -0
  84. package/src/Message/ListMessage/OrderedListMessage.tsx +16 -2
  85. package/src/Message/ListMessage/UnorderedListMessage.tsx +12 -2
  86. package/src/Message/Message.tsx +21 -181
  87. package/src/Message/TableMessage/TableMessage.scss +11 -0
  88. package/src/Message/TableMessage/TableMessage.tsx +18 -2
  89. package/src/Message/TextMessage/TextMessage.scss +8 -0
  90. package/src/Message/TextMessage/TextMessage.tsx +29 -2
  91. package/src/ToolCall/ToolCall.test.tsx +91 -0
  92. package/src/ToolCall/ToolCall.tsx +49 -4
  93. package/src/ToolResponse/ToolResponse.scss +10 -0
  94. package/src/ToolResponse/ToolResponse.test.tsx +119 -0
  95. package/src/ToolResponse/ToolResponse.tsx +82 -7
  96. package/src/index.ts +3 -0
  97. package/src/main.scss +1 -0
@@ -0,0 +1,174 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
13
+ import Markdown from 'react-markdown';
14
+ import remarkGfm from 'remark-gfm';
15
+ import { ContentVariants } from '@patternfly/react-core';
16
+ import CodeBlockMessage from '../Message/CodeBlockMessage/CodeBlockMessage';
17
+ import TextMessage from '../Message/TextMessage/TextMessage';
18
+ import ListItemMessage from '../Message/ListMessage/ListItemMessage';
19
+ import UnorderedListMessage from '../Message/ListMessage/UnorderedListMessage';
20
+ import OrderedListMessage from '../Message/ListMessage/OrderedListMessage';
21
+ import TableMessage from '../Message/TableMessage/TableMessage';
22
+ import TrMessage from '../Message/TableMessage/TrMessage';
23
+ import TdMessage from '../Message/TableMessage/TdMessage';
24
+ import TbodyMessage from '../Message/TableMessage/TbodyMessage';
25
+ import TheadMessage from '../Message/TableMessage/TheadMessage';
26
+ import ThMessage from '../Message/TableMessage/ThMessage';
27
+ import ImageMessage from '../Message/ImageMessage/ImageMessage';
28
+ import rehypeUnwrapImages from 'rehype-unwrap-images';
29
+ import rehypeExternalLinks from 'rehype-external-links';
30
+ import rehypeSanitize from 'rehype-sanitize';
31
+ import rehypeHighlight from 'rehype-highlight';
32
+ import 'highlight.js/styles/vs2015.css';
33
+ import LinkMessage from '../Message/LinkMessage/LinkMessage';
34
+ import { rehypeMoveImagesOutOfParagraphs } from '../Message/Plugins/rehypeMoveImagesOutOfParagraphs';
35
+ import SuperscriptMessage from '../Message/SuperscriptMessage/SuperscriptMessage';
36
+ import { css } from '@patternfly/react-styles';
37
+ export const MarkdownContent = ({ content, isMarkdownDisabled, codeBlockProps, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], additionalRemarkPlugins = [], linkProps, reactMarkdownProps, remarkGfmProps, hasNoImages = false, isPrimary, textComponent, shouldRetainStyles }) => {
38
+ let rehypePlugins = [rehypeUnwrapImages, rehypeMoveImagesOutOfParagraphs, rehypeHighlight];
39
+ if (openLinkInNewTab) {
40
+ rehypePlugins = rehypePlugins.concat([[rehypeExternalLinks, { target: '_blank' }, rehypeSanitize]]);
41
+ }
42
+ if (additionalRehypePlugins) {
43
+ rehypePlugins.push(...additionalRehypePlugins);
44
+ }
45
+ const disallowedElements = hasNoImages ? ['img'] : [];
46
+ if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
47
+ disallowedElements.push(...reactMarkdownProps.disallowedElements);
48
+ }
49
+ if (isMarkdownDisabled) {
50
+ if (textComponent) {
51
+ return _jsx(_Fragment, { children: textComponent });
52
+ }
53
+ return (_jsx(TextMessage, { component: ContentVariants.p, isPrimary: isPrimary, children: content }));
54
+ }
55
+ return (_jsx(Markdown, Object.assign({ components: {
56
+ section: (props) => {
57
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
+ const { node } = props, rest = __rest(props, ["node"]);
59
+ return (_jsx("section", Object.assign({}, rest, { className: css('pf-chatbot__message-text', shouldRetainStyles && 'pf-m-markdown', rest === null || rest === void 0 ? void 0 : rest.className) })));
60
+ },
61
+ p: (props) => {
62
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
63
+ const { node } = props, rest = __rest(props, ["node"]);
64
+ return (_jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.p }, rest, { isPrimary: isPrimary })));
65
+ },
66
+ code: (_a) => {
67
+ var { children } = _a, props = __rest(_a, ["children"]);
68
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
69
+ const { node } = props, codeProps = __rest(props, ["node"]);
70
+ return (_jsx(CodeBlockMessage, Object.assign({}, codeProps, codeBlockProps, { isPrimary: isPrimary, shouldRetainStyles: shouldRetainStyles, children: children })));
71
+ },
72
+ h1: (props) => {
73
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
74
+ const { node } = props, rest = __rest(props, ["node"]);
75
+ return _jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.h1 }, rest));
76
+ },
77
+ h2: (props) => {
78
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
79
+ const { node } = props, rest = __rest(props, ["node"]);
80
+ return _jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.h2 }, rest));
81
+ },
82
+ h3: (props) => {
83
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
84
+ const { node } = props, rest = __rest(props, ["node"]);
85
+ return _jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.h3 }, rest));
86
+ },
87
+ h4: (props) => {
88
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
89
+ const { node } = props, rest = __rest(props, ["node"]);
90
+ return _jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.h4 }, rest));
91
+ },
92
+ h5: (props) => {
93
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
94
+ const { node } = props, rest = __rest(props, ["node"]);
95
+ return _jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.h5 }, rest));
96
+ },
97
+ h6: (props) => {
98
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
99
+ const { node } = props, rest = __rest(props, ["node"]);
100
+ return _jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.h6 }, rest));
101
+ },
102
+ blockquote: (props) => {
103
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
104
+ const { node } = props, rest = __rest(props, ["node"]);
105
+ return (_jsx(TextMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: ContentVariants.blockquote }, rest)));
106
+ },
107
+ ul: (props) => {
108
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
109
+ const { node } = props, rest = __rest(props, ["node"]);
110
+ return _jsx(UnorderedListMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles }, rest));
111
+ },
112
+ ol: (props) => {
113
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
114
+ const { node } = props, rest = __rest(props, ["node"]);
115
+ return _jsx(OrderedListMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles }, rest));
116
+ },
117
+ li: (props) => {
118
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
119
+ const { node } = props, rest = __rest(props, ["node"]);
120
+ return _jsx(ListItemMessage, Object.assign({}, rest));
121
+ },
122
+ // table requires node attribute for calculating headers for mobile breakpoint
123
+ table: (props) => (_jsx(TableMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles }, props, tableProps, { isPrimary: isPrimary }))),
124
+ tbody: (props) => {
125
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
126
+ const { node } = props, rest = __rest(props, ["node"]);
127
+ return _jsx(TbodyMessage, Object.assign({}, rest));
128
+ },
129
+ thead: (props) => {
130
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
131
+ const { node } = props, rest = __rest(props, ["node"]);
132
+ return _jsx(TheadMessage, Object.assign({}, rest));
133
+ },
134
+ tr: (props) => {
135
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
136
+ const { node } = props, rest = __rest(props, ["node"]);
137
+ return _jsx(TrMessage, Object.assign({}, rest));
138
+ },
139
+ td: (props) => {
140
+ // Conflicts with Td type
141
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
142
+ const { node, width } = props, rest = __rest(props, ["node", "width"]);
143
+ return _jsx(TdMessage, Object.assign({}, rest));
144
+ },
145
+ th: (props) => {
146
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
147
+ const { node } = props, rest = __rest(props, ["node"]);
148
+ return _jsx(ThMessage, Object.assign({}, rest));
149
+ },
150
+ img: (props) => {
151
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
152
+ const { node } = props, rest = __rest(props, ["node"]);
153
+ return _jsx(ImageMessage, Object.assign({}, rest));
154
+ },
155
+ a: (props) => {
156
+ // node is just the details of the document structure - not needed
157
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
158
+ const { node } = props, rest = __rest(props, ["node"]);
159
+ return (
160
+ // some a types conflict with ButtonProps, but it's ok because we are using an a tag
161
+ // there are too many to handle manually
162
+ _jsx(LinkMessage, Object.assign({ shouldRetainStyles: shouldRetainStyles }, rest, linkProps, { children: props.children })));
163
+ },
164
+ // used for footnotes
165
+ sup: (props) => {
166
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
167
+ const { node } = props, rest = __rest(props, ["node"]);
168
+ return _jsx(SuperscriptMessage, Object.assign({}, rest));
169
+ }
170
+ }, remarkPlugins: [[remarkGfm, Object.assign({}, remarkGfmProps)], ...additionalRemarkPlugins], rehypePlugins: rehypePlugins }, reactMarkdownProps, { remarkRehypeOptions: Object.assign({
171
+ // removes sr-only class from footnote labels applied by default
172
+ footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), disallowedElements: disallowedElements, children: content })));
173
+ };
174
+ export default MarkdownContent;
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,187 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { jsx as _jsx } from "react/jsx-runtime";
11
+ import { render, screen } from '@testing-library/react';
12
+ import '@testing-library/jest-dom';
13
+ import MarkdownContent from './MarkdownContent';
14
+ import rehypeExternalLinks from '../__mocks__/rehype-external-links';
15
+ const BOLD_TEXT = '**Bold text**';
16
+ const ITALIC_TEXT = '*Italic text*';
17
+ const INLINE_CODE = 'Here is inline code: `const x = 5`';
18
+ const CODE_BLOCK = `\`\`\`javascript
19
+ function hello() {
20
+ console.log('Hello, world!');
21
+ }
22
+ \`\`\``;
23
+ const HEADING = '# Heading 1';
24
+ const LINK = '[PatternFly](https://www.patternfly.org/)';
25
+ const UNORDERED_LIST = `
26
+ * Item 1
27
+ * Item 2
28
+ * Item 3
29
+ `;
30
+ const ORDERED_LIST = `
31
+ 1. First item
32
+ 2. Second item
33
+ 3. Third item
34
+ `;
35
+ const TABLE = `
36
+ | Column 1 | Column 2 |
37
+ |----------|----------|
38
+ | Cell 1 | Cell 2 |
39
+ | Cell 3 | Cell 4 |
40
+ `;
41
+ const BLOCKQUOTE = '> This is a blockquote';
42
+ const IMAGE = '![Alt text](https://example.com/image.png)';
43
+ describe('MarkdownContent', () => {
44
+ beforeEach(() => {
45
+ jest.clearAllMocks();
46
+ });
47
+ it('should render bold text correctly', () => {
48
+ const { container } = render(_jsx(MarkdownContent, { content: BOLD_TEXT }));
49
+ expect(container.querySelector('strong')).toBeTruthy();
50
+ expect(screen.getByText('Bold text')).toBeTruthy();
51
+ });
52
+ it('should render italic text correctly', () => {
53
+ const { container } = render(_jsx(MarkdownContent, { content: ITALIC_TEXT }));
54
+ expect(container.querySelector('em')).toBeTruthy();
55
+ expect(screen.getByText('Italic text')).toBeTruthy();
56
+ });
57
+ it('should render inline code correctly', () => {
58
+ render(_jsx(MarkdownContent, { content: INLINE_CODE }));
59
+ expect(screen.getByText(/const x = 5/)).toBeTruthy();
60
+ });
61
+ it('should render code blocks correctly', () => {
62
+ render(_jsx(MarkdownContent, { content: CODE_BLOCK }));
63
+ expect(screen.getByText(/function hello/)).toBeVisible();
64
+ expect(screen.getByText(/console.log/)).toBeVisible();
65
+ expect(screen.getByRole('button', { name: 'Copy code' })).toBeVisible();
66
+ });
67
+ it('should render headings correctly', () => {
68
+ render(_jsx(MarkdownContent, { content: HEADING }));
69
+ expect(screen.getByRole('heading', { name: /Heading 1/i })).toBeTruthy();
70
+ });
71
+ it('should render links correctly', () => {
72
+ render(_jsx(MarkdownContent, { content: LINK }));
73
+ expect(screen.getByRole('link', { name: /PatternFly/i })).toBeTruthy();
74
+ });
75
+ it('should render unordered lists correctly', () => {
76
+ render(_jsx(MarkdownContent, { content: UNORDERED_LIST }));
77
+ expect(screen.getByText('Item 1')).toBeTruthy();
78
+ expect(screen.getByText('Item 2')).toBeTruthy();
79
+ expect(screen.getByText('Item 3')).toBeTruthy();
80
+ expect(screen.getAllByRole('listitem')).toHaveLength(3);
81
+ });
82
+ it('should render ordered lists correctly', () => {
83
+ render(_jsx(MarkdownContent, { content: ORDERED_LIST }));
84
+ expect(screen.getByText('First item')).toBeTruthy();
85
+ expect(screen.getByText('Second item')).toBeTruthy();
86
+ expect(screen.getByText('Third item')).toBeTruthy();
87
+ expect(screen.getAllByRole('listitem')).toHaveLength(3);
88
+ });
89
+ it('should render tables correctly', () => {
90
+ render(_jsx(MarkdownContent, { content: TABLE, tableProps: { 'aria-label': 'Test table' } }));
91
+ expect(screen.getByRole('grid', { name: /Test table/i })).toBeTruthy();
92
+ expect(screen.getByRole('columnheader', { name: /Column 1/i })).toBeTruthy();
93
+ expect(screen.getByRole('columnheader', { name: /Column 2/i })).toBeTruthy();
94
+ expect(screen.getByRole('cell', { name: /Cell 1/i })).toBeTruthy();
95
+ expect(screen.getByRole('cell', { name: /Cell 2/i })).toBeTruthy();
96
+ });
97
+ it('should render blockquotes correctly', () => {
98
+ var _a;
99
+ render(_jsx(MarkdownContent, { content: BLOCKQUOTE }));
100
+ const quote = screen.getByText(/This is a blockquote/);
101
+ expect(quote).toBeVisible();
102
+ expect((_a = quote.closest('.pf-v6-c-content--blockquote')) === null || _a === void 0 ? void 0 : _a.tagName).toBe('BLOCKQUOTE');
103
+ });
104
+ it('should render images when hasNoImages is false', () => {
105
+ render(_jsx(MarkdownContent, { content: IMAGE, hasNoImages: false }));
106
+ expect(screen.getByRole('img', { name: /Alt text/i })).toBeTruthy();
107
+ });
108
+ it('should not render images when hasNoImages is true', () => {
109
+ render(_jsx(MarkdownContent, { content: IMAGE, hasNoImages: true }));
110
+ expect(screen.queryByRole('img', { name: /Alt text/i })).toBeFalsy();
111
+ });
112
+ it('should disable markdown rendering when isMarkdownDisabled is true', () => {
113
+ render(_jsx(MarkdownContent, { content: BOLD_TEXT, isMarkdownDisabled: true }));
114
+ expect(screen.getByText('**Bold text**')).toBeTruthy();
115
+ });
116
+ it('should render text component when isMarkdownDisabled is true and textComponent is provided', () => {
117
+ const textComponent = _jsx("div", { "data-testid": "custom-text", children: "Custom text component" });
118
+ render(_jsx(MarkdownContent, { content: BOLD_TEXT, isMarkdownDisabled: true, textComponent: textComponent }));
119
+ expect(screen.getByTestId('custom-text')).toBeTruthy();
120
+ expect(screen.getByText('Custom text component')).toBeTruthy();
121
+ });
122
+ it('should apply isPrimary prop to elements', () => {
123
+ const { container } = render(_jsx(MarkdownContent, { content: INLINE_CODE, isPrimary: true }));
124
+ expect(container.querySelector('.pf-m-primary')).toBeTruthy();
125
+ });
126
+ it('should apply shouldRetainStyles prop to elements', () => {
127
+ const { container } = render(_jsx(MarkdownContent, { content: BOLD_TEXT, shouldRetainStyles: true }));
128
+ expect(container.querySelector('.pf-m-markdown')).toBeTruthy();
129
+ });
130
+ it('should pass codeBlockProps to code blocks', () => {
131
+ render(_jsx(MarkdownContent, { content: CODE_BLOCK, codeBlockProps: { 'aria-label': 'Custom code block' } }));
132
+ expect(screen.getByRole('button', { name: /Custom code block/i })).toBeTruthy();
133
+ });
134
+ it('should pass tableProps to tables', () => {
135
+ render(_jsx(MarkdownContent, { content: TABLE, tableProps: { 'aria-label': 'Custom table label' } }));
136
+ expect(screen.getByRole('grid', { name: /Custom table label/i })).toBeTruthy();
137
+ });
138
+ it('should open links in new tab when openLinkInNewTab is true', () => {
139
+ render(_jsx(MarkdownContent, { content: LINK, openLinkInNewTab: true }));
140
+ expect(rehypeExternalLinks).toHaveBeenCalledTimes(1);
141
+ });
142
+ it('should not open links in new tab when openLinkInNewTab is false', () => {
143
+ render(_jsx(MarkdownContent, { content: LINK, openLinkInNewTab: false }));
144
+ expect(rehypeExternalLinks).not.toHaveBeenCalled();
145
+ });
146
+ it('should pass linkProps to links', () => __awaiter(void 0, void 0, void 0, function* () {
147
+ const onClick = jest.fn();
148
+ render(_jsx(MarkdownContent, { content: LINK, linkProps: { onClick } }));
149
+ const link = screen.getByRole('link', { name: /PatternFly/i });
150
+ link.click();
151
+ expect(onClick).toHaveBeenCalledTimes(1);
152
+ }));
153
+ it('should handle reactMarkdownProps.disallowedElements', () => {
154
+ render(_jsx(MarkdownContent, { content: CODE_BLOCK, reactMarkdownProps: { disallowedElements: ['code'] } }));
155
+ // Code block should not render when disallowed
156
+ expect(screen.queryByRole('button', { name: /Copy code/i })).toBeFalsy();
157
+ });
158
+ it('should render plain text when no markdown is present', () => {
159
+ render(_jsx(MarkdownContent, { content: "Plain text without markdown" }));
160
+ expect(screen.getByText('Plain text without markdown')).toBeTruthy();
161
+ });
162
+ it('should handle empty content', () => {
163
+ const { container } = render(_jsx(MarkdownContent, { content: "" }));
164
+ expect(container.textContent).toBe('');
165
+ });
166
+ it('should handle undefined content', () => {
167
+ const { container } = render(_jsx(MarkdownContent, {}));
168
+ expect(container.textContent).toBe('');
169
+ });
170
+ it('should render multiple markdown elements together', () => {
171
+ const content = `# Heading
172
+
173
+ **Bold text** and *italic text*
174
+
175
+ \`\`\`javascript
176
+ const x = 5;
177
+ \`\`\`
178
+
179
+ [Link](https://example.com)`;
180
+ render(_jsx(MarkdownContent, { content: content }));
181
+ expect(screen.getByRole('heading', { name: /Heading/i })).toBeTruthy();
182
+ expect(screen.getByText('Bold text')).toBeTruthy();
183
+ expect(screen.getByText('italic text')).toBeTruthy();
184
+ expect(screen.getByText(/const x = 5/)).toBeTruthy();
185
+ expect(screen.getByRole('link', { name: /Link/i })).toBeTruthy();
186
+ });
187
+ });
@@ -0,0 +1,2 @@
1
+ export { default } from './MarkdownContent';
2
+ export * from './MarkdownContent';
@@ -0,0 +1,2 @@
1
+ export { default } from './MarkdownContent';
2
+ export * from './MarkdownContent';
@@ -20,6 +20,8 @@ export interface CodeBlockMessageProps {
20
20
  customActions?: React.ReactNode;
21
21
  /** Sets background colors to be appropriate on primary chatbot background */
22
22
  isPrimary?: boolean;
23
+ /** Flag indicating that the content should retain message styles when using Markdown. */
24
+ shouldRetainStyles?: boolean;
23
25
  }
24
- declare const CodeBlockMessage: ({ children, className, "aria-label": ariaLabel, isExpandable, expandableSectionProps, expandableSectionToggleProps, expandedText, collapsedText, customActions, isPrimary, ...props }: CodeBlockMessageProps) => import("react/jsx-runtime").JSX.Element;
26
+ declare const CodeBlockMessage: ({ children, className, "aria-label": ariaLabel, isExpandable, expandableSectionProps, expandableSectionToggleProps, expandedText, collapsedText, customActions, isPrimary, shouldRetainStyles, ...props }: CodeBlockMessageProps) => import("react/jsx-runtime").JSX.Element;
25
27
  export default CodeBlockMessage;
@@ -18,11 +18,12 @@ import { useState, useRef, useCallback, useEffect } from 'react';
18
18
  import { CodeBlock, CodeBlockAction, CodeBlockCode, Button, Tooltip, ExpandableSection, ExpandableSectionToggle, ExpandableSectionVariant, getUniqueId } from '@patternfly/react-core';
19
19
  import { CheckIcon } from '@patternfly/react-icons/dist/esm/icons/check-icon';
20
20
  import { CopyIcon } from '@patternfly/react-icons/dist/esm/icons/copy-icon';
21
+ import { css } from '@patternfly/react-styles';
21
22
  const DEFAULT_EXPANDED_TEXT = 'Show less';
22
23
  const DEFAULT_COLLAPSED_TEXT = 'Show more';
23
24
  const CodeBlockMessage = (_a) => {
24
25
  var _b;
25
- var { children, className, 'aria-label': ariaLabel, isExpandable = false, expandableSectionProps, expandableSectionToggleProps, expandedText = DEFAULT_EXPANDED_TEXT, collapsedText = DEFAULT_COLLAPSED_TEXT, customActions, isPrimary } = _a, props = __rest(_a, ["children", "className", 'aria-label', "isExpandable", "expandableSectionProps", "expandableSectionToggleProps", "expandedText", "collapsedText", "customActions", "isPrimary"]);
26
+ var { children, className, 'aria-label': ariaLabel, isExpandable = false, expandableSectionProps, expandableSectionToggleProps, expandedText = DEFAULT_EXPANDED_TEXT, collapsedText = DEFAULT_COLLAPSED_TEXT, customActions, isPrimary, shouldRetainStyles } = _a, props = __rest(_a, ["children", "className", 'aria-label', "isExpandable", "expandableSectionProps", "expandableSectionToggleProps", "expandedText", "collapsedText", "customActions", "isPrimary", "shouldRetainStyles"]);
26
27
  const [copied, setCopied] = useState(false);
27
28
  const [isExpanded, setIsExpanded] = useState(false);
28
29
  const buttonRef = useRef();
@@ -63,6 +64,6 @@ const CodeBlockMessage = (_a) => {
63
64
  }
64
65
  // Setup code block header
65
66
  const actions = (_jsxs(_Fragment, { children: [_jsxs(CodeBlockAction, { className: "pf-chatbot__message-code-block-default-action", children: [language && _jsx("div", { className: "pf-chatbot__message-code-block-language", children: language }), _jsx(Button, { ref: buttonRef, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : 'Copy code', variant: "plain", className: "pf-chatbot__button--copy", onClick: (event) => handleCopy(event, children), children: copied ? _jsx(CheckIcon, {}) : _jsx(CopyIcon, {}) }), _jsx(Tooltip, { id: tooltipID, content: "Copy", position: "top", triggerRef: buttonRef })] }), customActions] }));
66
- return (_jsx("div", { className: "pf-chatbot__message-code-block", ref: codeBlockRef, children: _jsxs(CodeBlock, { actions: actions, children: [_jsx(CodeBlockCode, { children: _jsx(_Fragment, { children: isExpandable ? (_jsx(ExpandableSection, Object.assign({ variant: ExpandableSectionVariant.truncate, isExpanded: isExpanded, isDetached: true, toggleId: toggleId, contentId: contentId }, expandableSectionProps, { children: children }))) : (children) }) }), isExpandable && (_jsx(ExpandableSectionToggle, Object.assign({ isExpanded: isExpanded, onToggle: onToggle, direction: "up", toggleId: toggleId, contentId: contentId, hasTruncatedContent: true, className: "pf-chatbot__message-code-toggle" }, expandableSectionToggleProps, { children: isExpanded ? finalExpandedText : finalCollapsedText })))] }) }));
67
+ return (_jsx("div", { className: css('pf-chatbot__message-code-block', shouldRetainStyles && 'pf-m-markdown'), ref: codeBlockRef, children: _jsxs(CodeBlock, { actions: actions, children: [_jsx(CodeBlockCode, { children: _jsx(_Fragment, { children: isExpandable ? (_jsx(ExpandableSection, Object.assign({ variant: ExpandableSectionVariant.truncate, isExpanded: isExpanded, isDetached: true, toggleId: toggleId, contentId: contentId }, expandableSectionProps, { children: children }))) : (children) }) }), isExpandable && (_jsx(ExpandableSectionToggle, Object.assign({ isExpanded: isExpanded, onToggle: onToggle, direction: "up", toggleId: toggleId, contentId: contentId, hasTruncatedContent: true, className: "pf-chatbot__message-code-toggle" }, expandableSectionToggleProps, { children: isExpanded ? finalExpandedText : finalCollapsedText })))] }) }));
67
68
  };
68
69
  export default CodeBlockMessage;
@@ -1,4 +1,8 @@
1
1
  import { ButtonProps } from '@patternfly/react-core';
2
2
  import { ExtraProps } from 'react-markdown';
3
- declare const LinkMessage: ({ children, target, href, id, ...props }: ButtonProps & ExtraProps) => import("react/jsx-runtime").JSX.Element;
3
+ export interface LinkMessageProps {
4
+ /** Flag indicating that the content should retain message styles when using Markdown. */
5
+ shouldRetainStyles?: boolean;
6
+ }
7
+ declare const LinkMessage: ({ children, target, href, id, shouldRetainStyles, ...props }: LinkMessageProps & ButtonProps & ExtraProps) => import("react/jsx-runtime").JSX.Element;
4
8
  export default LinkMessage;
@@ -15,15 +15,16 @@ import { jsx as _jsx } from "react/jsx-runtime";
15
15
  // ============================================================================
16
16
  import { Button } from '@patternfly/react-core';
17
17
  import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
18
+ import { css } from '@patternfly/react-styles';
18
19
  const LinkMessage = (_a) => {
19
- var { children, target, href, id } = _a, props = __rest(_a, ["children", "target", "href", "id"]);
20
+ var { children, target, href, id, shouldRetainStyles } = _a, props = __rest(_a, ["children", "target", "href", "id", "shouldRetainStyles"]);
20
21
  if (target === '_blank') {
21
22
  return (_jsx(Button, Object.assign({ component: "a", variant: "link", href: href, icon: _jsx(ExternalLinkSquareAltIcon, {}), iconPosition: "end", isInline: true, target: target,
22
23
  // need to explicitly call this out or id doesn't seem to get passed - required for footnotes
23
- id: id }, props, { children: children })));
24
+ id: id }, props, { className: css(shouldRetainStyles && 'pf-m-markdown', props === null || props === void 0 ? void 0 : props.className), children: children })));
24
25
  }
25
26
  return (
26
27
  // need to explicitly call this out or id doesn't seem to get passed - required for footnotes
27
- _jsx(Button, Object.assign({ isInline: true, component: "a", href: href, variant: "link", id: id }, props, { children: children })));
28
+ _jsx(Button, Object.assign({ isInline: true, component: "a", href: href, variant: "link", id: id }, props, { className: css(shouldRetainStyles && 'pf-m-markdown', props === null || props === void 0 ? void 0 : props.className), children: children })));
28
29
  };
29
30
  export default LinkMessage;
@@ -1,3 +1,11 @@
1
1
  import { ExtraProps } from 'react-markdown';
2
- declare const OrderedListMessage: ({ children, start }: JSX.IntrinsicElements["ol"] & ExtraProps) => import("react/jsx-runtime").JSX.Element;
2
+ export interface OrderedListMessageProps {
3
+ /** The ordered list content */
4
+ children?: React.ReactNode;
5
+ /** The number to start the ordered list at. */
6
+ start?: number;
7
+ /** Flag indicating that the content should retain message styles when using Markdown. */
8
+ shouldRetainStyles?: boolean;
9
+ }
10
+ declare const OrderedListMessage: ({ children, start, shouldRetainStyles }: OrderedListMessageProps & JSX.IntrinsicElements["ol"] & ExtraProps) => import("react/jsx-runtime").JSX.Element;
3
11
  export default OrderedListMessage;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { List, ListComponent, OrderType } from '@patternfly/react-core';
3
- const OrderedListMessage = ({ children, start }) => (_jsx("div", { className: "pf-chatbot__message-ordered-list", children: _jsx(List, { component: ListComponent.ol, type: OrderType.number, start: start, children: children }) }));
3
+ import { css } from '@patternfly/react-styles';
4
+ const OrderedListMessage = ({ children, start, shouldRetainStyles }) => (_jsx("div", { className: css('pf-chatbot__message-ordered-list', shouldRetainStyles && 'pf-m-markdown'), children: _jsx(List, { component: ListComponent.ol, type: OrderType.number, start: start, children: children }) }));
4
5
  export default OrderedListMessage;
@@ -1,3 +1,9 @@
1
1
  import { ExtraProps } from 'react-markdown';
2
- declare const UnorderedListMessage: ({ children }: JSX.IntrinsicElements["ul"] & ExtraProps) => import("react/jsx-runtime").JSX.Element;
2
+ export interface UnrderedListMessageProps {
3
+ /** The ordered list content */
4
+ children?: React.ReactNode;
5
+ /** Flag indicating that the content should retain message styles when using Markdown. */
6
+ shouldRetainStyles?: boolean;
7
+ }
8
+ declare const UnorderedListMessage: ({ children, shouldRetainStyles }: UnrderedListMessageProps & JSX.IntrinsicElements["ul"] & ExtraProps) => import("react/jsx-runtime").JSX.Element;
3
9
  export default UnorderedListMessage;
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { List } from '@patternfly/react-core';
3
- const UnorderedListMessage = ({ children }) => (_jsx("div", { className: "pf-chatbot__message-unordered-list", children: _jsx(List, { children: children }) }));
3
+ import { css } from '@patternfly/react-styles';
4
+ const UnorderedListMessage = ({ children, shouldRetainStyles }) => (_jsx("div", { className: css('pf-chatbot__message-unordered-list', shouldRetainStyles && 'pf-m-markdown'), children: _jsx(List, { children: children }) }));
4
5
  export default UnorderedListMessage;