@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
@@ -1,8 +1,11 @@
1
1
  import { CardBodyProps, CardProps, ExpandableSectionProps } from '@patternfly/react-core';
2
2
  import { type FunctionComponent } from 'react';
3
+ import type { MarkdownContentProps } from '../MarkdownContent';
3
4
  export interface DeepThinkingProps {
4
5
  /** Toggle content shown for expandable section */
5
6
  toggleContent: React.ReactNode;
7
+ /** Flag indicating whether the expandable content is expanded by default. */
8
+ isDefaultExpanded?: boolean;
6
9
  /** Additional props passed to expandable section */
7
10
  expandableSectionProps?: Omit<ExpandableSectionProps, 'ref'>;
8
11
  /** Subheading rendered inside expandable section */
@@ -13,6 +16,16 @@ export interface DeepThinkingProps {
13
16
  cardProps?: CardProps;
14
17
  /** Additional props passed to main card body */
15
18
  cardBodyProps?: CardBodyProps;
19
+ /** Whether to enable markdown rendering for toggleContent. When true and toggleContent is a string, it will be parsed as markdown. */
20
+ isToggleContentMarkdown?: boolean;
21
+ /** Whether to enable markdown rendering for subheading. When true, subheading will be parsed as markdown. */
22
+ isSubheadingMarkdown?: boolean;
23
+ /** Whether to enable markdown rendering for body. When true and body is a string, it will be parsed as markdown. */
24
+ isBodyMarkdown?: boolean;
25
+ /** Props passed to MarkdownContent component when markdown is enabled */
26
+ markdownContentProps?: Omit<MarkdownContentProps, 'content'>;
27
+ /** Whether to retain styles in the MarkdownContent component. Defaults to false. */
28
+ shouldRetainStyles?: boolean;
16
29
  }
17
30
  export declare const DeepThinking: FunctionComponent<DeepThinkingProps>;
18
31
  export default DeepThinking;
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.DeepThinking = void 0;
4
7
  const jsx_runtime_1 = require("react/jsx-runtime");
@@ -7,12 +10,37 @@ const jsx_runtime_1 = require("react/jsx-runtime");
7
10
  // ============================================================================
8
11
  const react_core_1 = require("@patternfly/react-core");
9
12
  const react_1 = require("react");
10
- const DeepThinking = ({ body, cardProps, expandableSectionProps, subheading, toggleContent, cardBodyProps }) => {
11
- const [isExpanded, setIsExpanded] = (0, react_1.useState)(true);
13
+ const MarkdownContent_1 = __importDefault(require("../MarkdownContent"));
14
+ const DeepThinking = ({ body, cardProps, expandableSectionProps, subheading, toggleContent, isDefaultExpanded = true, cardBodyProps, isToggleContentMarkdown, isSubheadingMarkdown, isBodyMarkdown, markdownContentProps, shouldRetainStyles = false }) => {
15
+ const [isExpanded, setIsExpanded] = (0, react_1.useState)(isDefaultExpanded);
12
16
  const onToggle = (_event, isExpanded) => {
13
17
  setIsExpanded(isExpanded);
14
18
  };
15
- return ((0, jsx_runtime_1.jsx)(react_core_1.Card, Object.assign({ isCompact: true, className: "pf-chatbot__deep-thinking" }, cardProps, { children: (0, jsx_runtime_1.jsx)(react_core_1.CardBody, Object.assign({}, cardBodyProps, { children: (0, jsx_runtime_1.jsx)(react_core_1.ExpandableSection, Object.assign({ toggleContent: toggleContent, onToggle: onToggle, isExpanded: isExpanded, isIndented: true, className: "pf-chatbot__deep-thinking-expandable-section" }, expandableSectionProps, { children: (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__deep-thinking-section", children: [subheading && ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__deep-thinking-subheading", children: (0, jsx_runtime_1.jsx)("span", { children: subheading }) })), body && (0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__deep-thinking-body", children: body })] }) })) })) })));
19
+ const renderToggleContent = () => {
20
+ if (isToggleContentMarkdown && typeof toggleContent === 'string') {
21
+ return ((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: toggleContent }, markdownContentProps)));
22
+ }
23
+ return toggleContent;
24
+ };
25
+ const renderSubheading = () => {
26
+ if (!subheading) {
27
+ return null;
28
+ }
29
+ if (isSubheadingMarkdown) {
30
+ return (0, jsx_runtime_1.jsx)(MarkdownContent_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: subheading }, markdownContentProps));
31
+ }
32
+ return subheading;
33
+ };
34
+ const renderBody = () => {
35
+ if (!body) {
36
+ return null;
37
+ }
38
+ if (isBodyMarkdown && typeof body === 'string') {
39
+ return (0, jsx_runtime_1.jsx)(MarkdownContent_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, content: body }, markdownContentProps));
40
+ }
41
+ return body;
42
+ };
43
+ return ((0, jsx_runtime_1.jsx)(react_core_1.Card, Object.assign({ isCompact: true, className: "pf-chatbot__deep-thinking" }, cardProps, { children: (0, jsx_runtime_1.jsx)(react_core_1.CardBody, Object.assign({}, cardBodyProps, { children: (0, jsx_runtime_1.jsx)(react_core_1.ExpandableSection, Object.assign({ toggleContent: renderToggleContent(), onToggle: onToggle, isExpanded: isExpanded, isIndented: true, className: "pf-chatbot__deep-thinking-expandable-section" }, expandableSectionProps, { children: (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__deep-thinking-section", children: [subheading && ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__deep-thinking-subheading", children: (0, jsx_runtime_1.jsx)("span", { children: renderSubheading() }) })), body && (0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__deep-thinking-body", children: renderBody() })] }) })) })) })));
16
44
  };
17
45
  exports.DeepThinking = DeepThinking;
18
46
  exports.default = exports.DeepThinking;
@@ -1,10 +1,20 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
13
  };
5
14
  Object.defineProperty(exports, "__esModule", { value: true });
6
15
  const jsx_runtime_1 = require("react/jsx-runtime");
7
16
  const react_1 = require("@testing-library/react");
17
+ const user_event_1 = __importDefault(require("@testing-library/user-event"));
8
18
  require("@testing-library/jest-dom");
9
19
  const DeepThinking_1 = __importDefault(require("./DeepThinking"));
10
20
  describe('DeepThinking', () => {
@@ -45,4 +55,74 @@ describe('DeepThinking', () => {
45
55
  const subheadingContainer = container.querySelector('.pf-chatbot__tool-response-subheading');
46
56
  expect(subheadingContainer).toBeFalsy();
47
57
  });
58
+ it('should pass through cardBodyProps', () => {
59
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { body: "Thinking content", cardBodyProps: { className: 'custom-card-body-class' } })));
60
+ const cardBody = react_1.screen.getByText('Thinking content').closest('.pf-v6-c-card__body');
61
+ expect(cardBody).toHaveClass('custom-card-body-class');
62
+ });
63
+ it('Renders expanded by default', () => {
64
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { body: "Thinking content" })));
65
+ expect(react_1.screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'true');
66
+ expect(react_1.screen.getByText('Thinking content')).toBeVisible();
67
+ });
68
+ it('Renders collapsed when isDefaultExpanded is false', () => {
69
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({ isDefaultExpanded: false }, defaultProps, { body: "Thinking content" })));
70
+ expect(react_1.screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'false');
71
+ expect(react_1.screen.getByText('Thinking content')).not.toBeVisible();
72
+ });
73
+ it('expandableSectionProps.isExpanded overrides isDefaultExpanded', () => {
74
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { isDefaultExpanded: false, body: "Thinking content", expandableSectionProps: { isExpanded: true } })));
75
+ expect(react_1.screen.getByRole('button', { name: defaultProps.toggleContent })).toHaveAttribute('aria-expanded', 'true');
76
+ expect(react_1.screen.getByText('Thinking content')).toBeVisible();
77
+ });
78
+ it('expandableSectionProps.onToggle overrides internal onToggle behavior', () => __awaiter(void 0, void 0, void 0, function* () {
79
+ const user = user_event_1.default.setup();
80
+ const customOnToggle = jest.fn();
81
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { isDefaultExpanded: false, body: "Thinking content", expandableSectionProps: { onToggle: customOnToggle } })));
82
+ const toggleButton = react_1.screen.getByRole('button', { name: defaultProps.toggleContent });
83
+ expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
84
+ yield user.click(toggleButton);
85
+ expect(customOnToggle).toHaveBeenCalled();
86
+ expect(toggleButton).toHaveAttribute('aria-expanded', 'false');
87
+ expect(react_1.screen.getByText('Thinking content')).not.toBeVisible();
88
+ }));
89
+ it('should render toggleContent as markdown when isToggleContentMarkdown is true', () => {
90
+ const toggleContent = '**Bold thinking**';
91
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, { toggleContent: toggleContent, isToggleContentMarkdown: true }));
92
+ expect(container.querySelector('strong')).toBeTruthy();
93
+ expect(react_1.screen.getByText('Bold thinking')).toBeTruthy();
94
+ });
95
+ it('should not render toggleContent as markdown when isToggleContentMarkdown is false', () => {
96
+ const toggleContent = '**Bold thinking**';
97
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, { toggleContent: toggleContent }));
98
+ expect(container.querySelector('strong')).toBeFalsy();
99
+ expect(react_1.screen.getByText('**Bold thinking**')).toBeTruthy();
100
+ });
101
+ it('should render subheading as markdown when isSubheadingMarkdown is true', () => {
102
+ const subheading = '**Bold subheading**';
103
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { subheading: subheading, isSubheadingMarkdown: true })));
104
+ expect(container.querySelector('strong')).toBeTruthy();
105
+ expect(react_1.screen.getByText('Bold subheading')).toBeTruthy();
106
+ });
107
+ it('should not render subheading as markdown when isSubheadingMarkdown is false', () => {
108
+ const subheading = '**Bold subheading**';
109
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { subheading: subheading })));
110
+ expect(react_1.screen.getByText('**Bold subheading**')).toBeTruthy();
111
+ });
112
+ it('should render body as markdown when isBodyMarkdown is true', () => {
113
+ const body = '**Bold body**';
114
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { body: body, isBodyMarkdown: true })));
115
+ expect(container.querySelector('strong')).toBeTruthy();
116
+ expect(react_1.screen.getByText('Bold body')).toBeTruthy();
117
+ });
118
+ it('should not render body as markdown when isBodyMarkdown is false', () => {
119
+ const body = '**Bold body**';
120
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { body: body })));
121
+ expect(react_1.screen.getByText('**Bold body**')).toBeTruthy();
122
+ });
123
+ it('should pass markdownContentProps to MarkdownContent component', () => {
124
+ const body = '**Bold body**';
125
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, defaultProps, { body: body, isBodyMarkdown: true, markdownContentProps: { isPrimary: true } })));
126
+ expect(container.querySelector('.pf-m-primary')).toBeTruthy();
127
+ });
48
128
  });
@@ -0,0 +1,39 @@
1
+ import { type FunctionComponent, ReactNode } from 'react';
2
+ import { Options } from 'react-markdown';
3
+ import { CodeBlockMessageProps } from '../Message/CodeBlockMessage/CodeBlockMessage';
4
+ import { TableProps } from '@patternfly/react-table';
5
+ import 'highlight.js/styles/vs2015.css';
6
+ import { PluggableList } from 'unified';
7
+ import { ButtonProps } from '@patternfly/react-core';
8
+ export interface MarkdownContentProps {
9
+ /** The markdown content to render */
10
+ content?: string;
11
+ /** Disables markdown parsing, allowing only text input */
12
+ isMarkdownDisabled?: boolean;
13
+ /** Props for code blocks */
14
+ codeBlockProps?: CodeBlockMessageProps;
15
+ /** Props for table message. It is important to include a detailed aria-label that describes the purpose of the table. */
16
+ tableProps?: Required<Pick<TableProps, 'aria-label'>> & TableProps;
17
+ /** Additional rehype plugins passed from the consumer */
18
+ additionalRehypePlugins?: PluggableList;
19
+ /** Additional remark plugins passed from the consumer */
20
+ additionalRemarkPlugins?: PluggableList;
21
+ /** Whether to open links in message in new tab. */
22
+ openLinkInNewTab?: boolean;
23
+ /** Props for links */
24
+ linkProps?: ButtonProps;
25
+ /** Allows passing additional props down to markdown parser react-markdown, such as allowedElements and disallowedElements. See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#options for options */
26
+ reactMarkdownProps?: Options;
27
+ /** Allows passing additional props down to remark-gfm. See https://github.com/remarkjs/remark-gfm?tab=readme-ov-file#options for options */
28
+ remarkGfmProps?: Options;
29
+ /** Whether to strip out images in markdown */
30
+ hasNoImages?: boolean;
31
+ /** Sets background colors to be appropriate on primary chatbot background */
32
+ isPrimary?: boolean;
33
+ /** Custom component to render when markdown is disabled */
34
+ textComponent?: ReactNode;
35
+ /** Flag indicating whether content should retain various styles of its context (typically font-size and text color). */
36
+ shouldRetainStyles?: boolean;
37
+ }
38
+ export declare const MarkdownContent: FunctionComponent<MarkdownContentProps>;
39
+ export default MarkdownContent;
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.MarkdownContent = void 0;
18
+ const jsx_runtime_1 = require("react/jsx-runtime");
19
+ const react_markdown_1 = __importDefault(require("react-markdown"));
20
+ const remark_gfm_1 = __importDefault(require("remark-gfm"));
21
+ const react_core_1 = require("@patternfly/react-core");
22
+ const CodeBlockMessage_1 = __importDefault(require("../Message/CodeBlockMessage/CodeBlockMessage"));
23
+ const TextMessage_1 = __importDefault(require("../Message/TextMessage/TextMessage"));
24
+ const ListItemMessage_1 = __importDefault(require("../Message/ListMessage/ListItemMessage"));
25
+ const UnorderedListMessage_1 = __importDefault(require("../Message/ListMessage/UnorderedListMessage"));
26
+ const OrderedListMessage_1 = __importDefault(require("../Message/ListMessage/OrderedListMessage"));
27
+ const TableMessage_1 = __importDefault(require("../Message/TableMessage/TableMessage"));
28
+ const TrMessage_1 = __importDefault(require("../Message/TableMessage/TrMessage"));
29
+ const TdMessage_1 = __importDefault(require("../Message/TableMessage/TdMessage"));
30
+ const TbodyMessage_1 = __importDefault(require("../Message/TableMessage/TbodyMessage"));
31
+ const TheadMessage_1 = __importDefault(require("../Message/TableMessage/TheadMessage"));
32
+ const ThMessage_1 = __importDefault(require("../Message/TableMessage/ThMessage"));
33
+ const ImageMessage_1 = __importDefault(require("../Message/ImageMessage/ImageMessage"));
34
+ const rehype_unwrap_images_1 = __importDefault(require("rehype-unwrap-images"));
35
+ const rehype_external_links_1 = __importDefault(require("rehype-external-links"));
36
+ const rehype_sanitize_1 = __importDefault(require("rehype-sanitize"));
37
+ const rehype_highlight_1 = __importDefault(require("rehype-highlight"));
38
+ require("highlight.js/styles/vs2015.css");
39
+ const LinkMessage_1 = __importDefault(require("../Message/LinkMessage/LinkMessage"));
40
+ const rehypeMoveImagesOutOfParagraphs_1 = require("../Message/Plugins/rehypeMoveImagesOutOfParagraphs");
41
+ const SuperscriptMessage_1 = __importDefault(require("../Message/SuperscriptMessage/SuperscriptMessage"));
42
+ const react_styles_1 = require("@patternfly/react-styles");
43
+ const MarkdownContent = ({ content, isMarkdownDisabled, codeBlockProps, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], additionalRemarkPlugins = [], linkProps, reactMarkdownProps, remarkGfmProps, hasNoImages = false, isPrimary, textComponent, shouldRetainStyles }) => {
44
+ let rehypePlugins = [rehype_unwrap_images_1.default, rehypeMoveImagesOutOfParagraphs_1.rehypeMoveImagesOutOfParagraphs, rehype_highlight_1.default];
45
+ if (openLinkInNewTab) {
46
+ rehypePlugins = rehypePlugins.concat([[rehype_external_links_1.default, { target: '_blank' }, rehype_sanitize_1.default]]);
47
+ }
48
+ if (additionalRehypePlugins) {
49
+ rehypePlugins.push(...additionalRehypePlugins);
50
+ }
51
+ const disallowedElements = hasNoImages ? ['img'] : [];
52
+ if (reactMarkdownProps && reactMarkdownProps.disallowedElements) {
53
+ disallowedElements.push(...reactMarkdownProps.disallowedElements);
54
+ }
55
+ if (isMarkdownDisabled) {
56
+ if (textComponent) {
57
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: textComponent });
58
+ }
59
+ return ((0, jsx_runtime_1.jsx)(TextMessage_1.default, { component: react_core_1.ContentVariants.p, isPrimary: isPrimary, children: content }));
60
+ }
61
+ return ((0, jsx_runtime_1.jsx)(react_markdown_1.default, Object.assign({ components: {
62
+ section: (props) => {
63
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
+ const { node } = props, rest = __rest(props, ["node"]);
65
+ return ((0, jsx_runtime_1.jsx)("section", Object.assign({}, rest, { className: (0, react_styles_1.css)('pf-chatbot__message-text', shouldRetainStyles && 'pf-m-markdown', rest === null || rest === void 0 ? void 0 : rest.className) })));
66
+ },
67
+ p: (props) => {
68
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
69
+ const { node } = props, rest = __rest(props, ["node"]);
70
+ return ((0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.p }, rest, { isPrimary: isPrimary })));
71
+ },
72
+ code: (_a) => {
73
+ var { children } = _a, props = __rest(_a, ["children"]);
74
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
75
+ const { node } = props, codeProps = __rest(props, ["node"]);
76
+ return ((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, Object.assign({}, codeProps, codeBlockProps, { isPrimary: isPrimary, shouldRetainStyles: shouldRetainStyles, children: children })));
77
+ },
78
+ h1: (props) => {
79
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
80
+ const { node } = props, rest = __rest(props, ["node"]);
81
+ return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.h1 }, rest));
82
+ },
83
+ h2: (props) => {
84
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
85
+ const { node } = props, rest = __rest(props, ["node"]);
86
+ return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.h2 }, rest));
87
+ },
88
+ h3: (props) => {
89
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
90
+ const { node } = props, rest = __rest(props, ["node"]);
91
+ return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.h3 }, rest));
92
+ },
93
+ h4: (props) => {
94
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
95
+ const { node } = props, rest = __rest(props, ["node"]);
96
+ return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.h4 }, rest));
97
+ },
98
+ h5: (props) => {
99
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
100
+ const { node } = props, rest = __rest(props, ["node"]);
101
+ return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.h5 }, rest));
102
+ },
103
+ h6: (props) => {
104
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
105
+ const { node } = props, rest = __rest(props, ["node"]);
106
+ return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.h6 }, rest));
107
+ },
108
+ blockquote: (props) => {
109
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
110
+ const { node } = props, rest = __rest(props, ["node"]);
111
+ return ((0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles, component: react_core_1.ContentVariants.blockquote }, rest)));
112
+ },
113
+ ul: (props) => {
114
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
115
+ const { node } = props, rest = __rest(props, ["node"]);
116
+ return (0, jsx_runtime_1.jsx)(UnorderedListMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles }, rest));
117
+ },
118
+ ol: (props) => {
119
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
120
+ const { node } = props, rest = __rest(props, ["node"]);
121
+ return (0, jsx_runtime_1.jsx)(OrderedListMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles }, rest));
122
+ },
123
+ li: (props) => {
124
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
125
+ const { node } = props, rest = __rest(props, ["node"]);
126
+ return (0, jsx_runtime_1.jsx)(ListItemMessage_1.default, Object.assign({}, rest));
127
+ },
128
+ // table requires node attribute for calculating headers for mobile breakpoint
129
+ table: (props) => ((0, jsx_runtime_1.jsx)(TableMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles }, props, tableProps, { isPrimary: isPrimary }))),
130
+ tbody: (props) => {
131
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
132
+ const { node } = props, rest = __rest(props, ["node"]);
133
+ return (0, jsx_runtime_1.jsx)(TbodyMessage_1.default, Object.assign({}, rest));
134
+ },
135
+ thead: (props) => {
136
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
137
+ const { node } = props, rest = __rest(props, ["node"]);
138
+ return (0, jsx_runtime_1.jsx)(TheadMessage_1.default, Object.assign({}, rest));
139
+ },
140
+ tr: (props) => {
141
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
142
+ const { node } = props, rest = __rest(props, ["node"]);
143
+ return (0, jsx_runtime_1.jsx)(TrMessage_1.default, Object.assign({}, rest));
144
+ },
145
+ td: (props) => {
146
+ // Conflicts with Td type
147
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
148
+ const { node, width } = props, rest = __rest(props, ["node", "width"]);
149
+ return (0, jsx_runtime_1.jsx)(TdMessage_1.default, Object.assign({}, rest));
150
+ },
151
+ th: (props) => {
152
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
153
+ const { node } = props, rest = __rest(props, ["node"]);
154
+ return (0, jsx_runtime_1.jsx)(ThMessage_1.default, Object.assign({}, rest));
155
+ },
156
+ img: (props) => {
157
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
158
+ const { node } = props, rest = __rest(props, ["node"]);
159
+ return (0, jsx_runtime_1.jsx)(ImageMessage_1.default, Object.assign({}, rest));
160
+ },
161
+ a: (props) => {
162
+ // node is just the details of the document structure - not needed
163
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
164
+ const { node } = props, rest = __rest(props, ["node"]);
165
+ return (
166
+ // some a types conflict with ButtonProps, but it's ok because we are using an a tag
167
+ // there are too many to handle manually
168
+ (0, jsx_runtime_1.jsx)(LinkMessage_1.default, Object.assign({ shouldRetainStyles: shouldRetainStyles }, rest, linkProps, { children: props.children })));
169
+ },
170
+ // used for footnotes
171
+ sup: (props) => {
172
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
173
+ const { node } = props, rest = __rest(props, ["node"]);
174
+ return (0, jsx_runtime_1.jsx)(SuperscriptMessage_1.default, Object.assign({}, rest));
175
+ }
176
+ }, remarkPlugins: [[remark_gfm_1.default, Object.assign({}, remarkGfmProps)], ...additionalRemarkPlugins], rehypePlugins: rehypePlugins }, reactMarkdownProps, { remarkRehypeOptions: Object.assign({
177
+ // removes sr-only class from footnote labels applied by default
178
+ footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), disallowedElements: disallowedElements, children: content })));
179
+ };
180
+ exports.MarkdownContent = MarkdownContent;
181
+ exports.default = exports.MarkdownContent;
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const jsx_runtime_1 = require("react/jsx-runtime");
16
+ const react_1 = require("@testing-library/react");
17
+ require("@testing-library/jest-dom");
18
+ const MarkdownContent_1 = __importDefault(require("./MarkdownContent"));
19
+ const rehype_external_links_1 = __importDefault(require("../__mocks__/rehype-external-links"));
20
+ const BOLD_TEXT = '**Bold text**';
21
+ const ITALIC_TEXT = '*Italic text*';
22
+ const INLINE_CODE = 'Here is inline code: `const x = 5`';
23
+ const CODE_BLOCK = `\`\`\`javascript
24
+ function hello() {
25
+ console.log('Hello, world!');
26
+ }
27
+ \`\`\``;
28
+ const HEADING = '# Heading 1';
29
+ const LINK = '[PatternFly](https://www.patternfly.org/)';
30
+ const UNORDERED_LIST = `
31
+ * Item 1
32
+ * Item 2
33
+ * Item 3
34
+ `;
35
+ const ORDERED_LIST = `
36
+ 1. First item
37
+ 2. Second item
38
+ 3. Third item
39
+ `;
40
+ const TABLE = `
41
+ | Column 1 | Column 2 |
42
+ |----------|----------|
43
+ | Cell 1 | Cell 2 |
44
+ | Cell 3 | Cell 4 |
45
+ `;
46
+ const BLOCKQUOTE = '> This is a blockquote';
47
+ const IMAGE = '![Alt text](https://example.com/image.png)';
48
+ describe('MarkdownContent', () => {
49
+ beforeEach(() => {
50
+ jest.clearAllMocks();
51
+ });
52
+ it('should render bold text correctly', () => {
53
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: BOLD_TEXT }));
54
+ expect(container.querySelector('strong')).toBeTruthy();
55
+ expect(react_1.screen.getByText('Bold text')).toBeTruthy();
56
+ });
57
+ it('should render italic text correctly', () => {
58
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: ITALIC_TEXT }));
59
+ expect(container.querySelector('em')).toBeTruthy();
60
+ expect(react_1.screen.getByText('Italic text')).toBeTruthy();
61
+ });
62
+ it('should render inline code correctly', () => {
63
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: INLINE_CODE }));
64
+ expect(react_1.screen.getByText(/const x = 5/)).toBeTruthy();
65
+ });
66
+ it('should render code blocks correctly', () => {
67
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: CODE_BLOCK }));
68
+ expect(react_1.screen.getByText(/function hello/)).toBeVisible();
69
+ expect(react_1.screen.getByText(/console.log/)).toBeVisible();
70
+ expect(react_1.screen.getByRole('button', { name: 'Copy code' })).toBeVisible();
71
+ });
72
+ it('should render headings correctly', () => {
73
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: HEADING }));
74
+ expect(react_1.screen.getByRole('heading', { name: /Heading 1/i })).toBeTruthy();
75
+ });
76
+ it('should render links correctly', () => {
77
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: LINK }));
78
+ expect(react_1.screen.getByRole('link', { name: /PatternFly/i })).toBeTruthy();
79
+ });
80
+ it('should render unordered lists correctly', () => {
81
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: UNORDERED_LIST }));
82
+ expect(react_1.screen.getByText('Item 1')).toBeTruthy();
83
+ expect(react_1.screen.getByText('Item 2')).toBeTruthy();
84
+ expect(react_1.screen.getByText('Item 3')).toBeTruthy();
85
+ expect(react_1.screen.getAllByRole('listitem')).toHaveLength(3);
86
+ });
87
+ it('should render ordered lists correctly', () => {
88
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: ORDERED_LIST }));
89
+ expect(react_1.screen.getByText('First item')).toBeTruthy();
90
+ expect(react_1.screen.getByText('Second item')).toBeTruthy();
91
+ expect(react_1.screen.getByText('Third item')).toBeTruthy();
92
+ expect(react_1.screen.getAllByRole('listitem')).toHaveLength(3);
93
+ });
94
+ it('should render tables correctly', () => {
95
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: TABLE, tableProps: { 'aria-label': 'Test table' } }));
96
+ expect(react_1.screen.getByRole('grid', { name: /Test table/i })).toBeTruthy();
97
+ expect(react_1.screen.getByRole('columnheader', { name: /Column 1/i })).toBeTruthy();
98
+ expect(react_1.screen.getByRole('columnheader', { name: /Column 2/i })).toBeTruthy();
99
+ expect(react_1.screen.getByRole('cell', { name: /Cell 1/i })).toBeTruthy();
100
+ expect(react_1.screen.getByRole('cell', { name: /Cell 2/i })).toBeTruthy();
101
+ });
102
+ it('should render blockquotes correctly', () => {
103
+ var _a;
104
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: BLOCKQUOTE }));
105
+ const quote = react_1.screen.getByText(/This is a blockquote/);
106
+ expect(quote).toBeVisible();
107
+ expect((_a = quote.closest('.pf-v6-c-content--blockquote')) === null || _a === void 0 ? void 0 : _a.tagName).toBe('BLOCKQUOTE');
108
+ });
109
+ it('should render images when hasNoImages is false', () => {
110
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: IMAGE, hasNoImages: false }));
111
+ expect(react_1.screen.getByRole('img', { name: /Alt text/i })).toBeTruthy();
112
+ });
113
+ it('should not render images when hasNoImages is true', () => {
114
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: IMAGE, hasNoImages: true }));
115
+ expect(react_1.screen.queryByRole('img', { name: /Alt text/i })).toBeFalsy();
116
+ });
117
+ it('should disable markdown rendering when isMarkdownDisabled is true', () => {
118
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: BOLD_TEXT, isMarkdownDisabled: true }));
119
+ expect(react_1.screen.getByText('**Bold text**')).toBeTruthy();
120
+ });
121
+ it('should render text component when isMarkdownDisabled is true and textComponent is provided', () => {
122
+ const textComponent = (0, jsx_runtime_1.jsx)("div", { "data-testid": "custom-text", children: "Custom text component" });
123
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: BOLD_TEXT, isMarkdownDisabled: true, textComponent: textComponent }));
124
+ expect(react_1.screen.getByTestId('custom-text')).toBeTruthy();
125
+ expect(react_1.screen.getByText('Custom text component')).toBeTruthy();
126
+ });
127
+ it('should apply isPrimary prop to elements', () => {
128
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: INLINE_CODE, isPrimary: true }));
129
+ expect(container.querySelector('.pf-m-primary')).toBeTruthy();
130
+ });
131
+ it('should apply shouldRetainStyles prop to elements', () => {
132
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: BOLD_TEXT, shouldRetainStyles: true }));
133
+ expect(container.querySelector('.pf-m-markdown')).toBeTruthy();
134
+ });
135
+ it('should pass codeBlockProps to code blocks', () => {
136
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: CODE_BLOCK, codeBlockProps: { 'aria-label': 'Custom code block' } }));
137
+ expect(react_1.screen.getByRole('button', { name: /Custom code block/i })).toBeTruthy();
138
+ });
139
+ it('should pass tableProps to tables', () => {
140
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: TABLE, tableProps: { 'aria-label': 'Custom table label' } }));
141
+ expect(react_1.screen.getByRole('grid', { name: /Custom table label/i })).toBeTruthy();
142
+ });
143
+ it('should open links in new tab when openLinkInNewTab is true', () => {
144
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: LINK, openLinkInNewTab: true }));
145
+ expect(rehype_external_links_1.default).toHaveBeenCalledTimes(1);
146
+ });
147
+ it('should not open links in new tab when openLinkInNewTab is false', () => {
148
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: LINK, openLinkInNewTab: false }));
149
+ expect(rehype_external_links_1.default).not.toHaveBeenCalled();
150
+ });
151
+ it('should pass linkProps to links', () => __awaiter(void 0, void 0, void 0, function* () {
152
+ const onClick = jest.fn();
153
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: LINK, linkProps: { onClick } }));
154
+ const link = react_1.screen.getByRole('link', { name: /PatternFly/i });
155
+ link.click();
156
+ expect(onClick).toHaveBeenCalledTimes(1);
157
+ }));
158
+ it('should handle reactMarkdownProps.disallowedElements', () => {
159
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: CODE_BLOCK, reactMarkdownProps: { disallowedElements: ['code'] } }));
160
+ // Code block should not render when disallowed
161
+ expect(react_1.screen.queryByRole('button', { name: /Copy code/i })).toBeFalsy();
162
+ });
163
+ it('should render plain text when no markdown is present', () => {
164
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: "Plain text without markdown" }));
165
+ expect(react_1.screen.getByText('Plain text without markdown')).toBeTruthy();
166
+ });
167
+ it('should handle empty content', () => {
168
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: "" }));
169
+ expect(container.textContent).toBe('');
170
+ });
171
+ it('should handle undefined content', () => {
172
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, {}));
173
+ expect(container.textContent).toBe('');
174
+ });
175
+ it('should render multiple markdown elements together', () => {
176
+ const content = `# Heading
177
+
178
+ **Bold text** and *italic text*
179
+
180
+ \`\`\`javascript
181
+ const x = 5;
182
+ \`\`\`
183
+
184
+ [Link](https://example.com)`;
185
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(MarkdownContent_1.default, { content: content }));
186
+ expect(react_1.screen.getByRole('heading', { name: /Heading/i })).toBeTruthy();
187
+ expect(react_1.screen.getByText('Bold text')).toBeTruthy();
188
+ expect(react_1.screen.getByText('italic text')).toBeTruthy();
189
+ expect(react_1.screen.getByText(/const x = 5/)).toBeTruthy();
190
+ expect(react_1.screen.getByRole('link', { name: /Link/i })).toBeTruthy();
191
+ });
192
+ });
@@ -0,0 +1,2 @@
1
+ export { default } from './MarkdownContent';
2
+ export * from './MarkdownContent';
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.default = void 0;
21
+ var MarkdownContent_1 = require("./MarkdownContent");
22
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(MarkdownContent_1).default; } });
23
+ __exportStar(require("./MarkdownContent"), exports);
@@ -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;