@patternfly/chatbot 6.5.0-prerelease.24 → 6.5.0-prerelease.25

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.
@@ -48,8 +48,18 @@ const CodeBlockMessage = (_a) => {
48
48
  setIsExpanded(isExpanded);
49
49
  };
50
50
  // Handle clicking copy button
51
- const handleCopy = (0, react_1.useCallback)((event, text) => {
52
- navigator.clipboard.writeText(text.toString());
51
+ const handleCopy = (0, react_1.useCallback)((_event, text) => {
52
+ let textToCopy = '';
53
+ if (typeof text === 'string') {
54
+ textToCopy = text;
55
+ }
56
+ else {
57
+ if (codeBlockRef.current) {
58
+ const codeElement = codeBlockRef.current.querySelector('code');
59
+ textToCopy = (codeElement === null || codeElement === void 0 ? void 0 : codeElement.textContent) || '';
60
+ }
61
+ }
62
+ navigator.clipboard.writeText(textToCopy);
53
63
  setCopied(true);
54
64
  }, []);
55
65
  // Reset copied state
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,131 @@
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
+ require("@testing-library/jest-dom");
17
+ const react_1 = require("@testing-library/react");
18
+ const user_event_1 = __importDefault(require("@testing-library/user-event"));
19
+ const CodeBlockMessage_1 = __importDefault(require("./CodeBlockMessage"));
20
+ // Mock clipboard API
21
+ Object.assign(navigator, {
22
+ clipboard: {
23
+ writeText: jest.fn()
24
+ }
25
+ });
26
+ describe('CodeBlockMessage', () => {
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+ });
30
+ it('should render inline code for single-line content', () => {
31
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { className: "language-javascript", children: "const x = 5;" }));
32
+ const code = react_1.screen.getByText('const x = 5;');
33
+ expect(code.tagName).toBe('CODE');
34
+ expect(code).toHaveClass('pf-chatbot__message-inline-code');
35
+ });
36
+ it('should render code block for multi-line content', () => {
37
+ const multilineCode = 'const x = 5;\nconst y = 10;';
38
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { className: "language-javascript", children: multilineCode }));
39
+ const codeElement = container.querySelector('code');
40
+ expect(codeElement === null || codeElement === void 0 ? void 0 : codeElement.textContent).toBe(multilineCode);
41
+ });
42
+ it('should display language label', () => {
43
+ const code = 'const x = 5;\nconst y = 10;';
44
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { className: "language-javascript", children: code }));
45
+ expect(react_1.screen.getByText('javascript')).toBeInTheDocument();
46
+ });
47
+ it('should render copy button', () => {
48
+ const code = 'const x = 5;\nconst y = 10;';
49
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { children: code }));
50
+ expect(react_1.screen.getByRole('button', { name: 'Copy code' })).toBeInTheDocument();
51
+ });
52
+ it('should copy plain string content to clipboard', () => __awaiter(void 0, void 0, void 0, function* () {
53
+ const code = 'const x = 5;\nconst y = 10;';
54
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { children: code }));
55
+ const copyButton = react_1.screen.getByRole('button', { name: 'Copy code' });
56
+ yield user_event_1.default.click(copyButton);
57
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(code);
58
+ }));
59
+ it('should extract text content from React elements when copying', () => __awaiter(void 0, void 0, void 0, function* () {
60
+ // Simulate what happens with syntax highlighting - children become React elements
61
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsxs)(CodeBlockMessage_1.default, { className: "language-javascript", children: [(0, jsx_runtime_1.jsx)("span", { className: "hljs-keyword", children: "const" }), " x = 5;", '\n', (0, jsx_runtime_1.jsx)("span", { className: "hljs-keyword", children: "const" }), " y = 10;"] }));
62
+ const copyButton = react_1.screen.getByRole('button', { name: 'Copy code' });
63
+ yield user_event_1.default.click(copyButton);
64
+ // Should extract actual text content from DOM, not "[object Object]"
65
+ const codeElement = container.querySelector('code');
66
+ const expectedText = (codeElement === null || codeElement === void 0 ? void 0 : codeElement.textContent) || '';
67
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedText);
68
+ expect(expectedText).not.toContain('[object Object]');
69
+ }));
70
+ it('should show check icon after copying', () => __awaiter(void 0, void 0, void 0, function* () {
71
+ const code = 'const x = 5;\nconst y = 10;';
72
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { children: code }));
73
+ const copyButton = react_1.screen.getByRole('button', { name: 'Copy code' });
74
+ yield user_event_1.default.click(copyButton);
75
+ // Check icon should be visible (we can verify by checking if CopyIcon is not present)
76
+ const svgElement = copyButton.querySelector('svg');
77
+ expect(svgElement).toBeInTheDocument();
78
+ }));
79
+ it('should render expandable section when isExpandable is true', () => {
80
+ const code = 'const x = 5;\nconst y = 10;';
81
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { isExpandable: true, children: code }));
82
+ expect(react_1.screen.getByRole('button', { name: 'Show more' })).toBeInTheDocument();
83
+ });
84
+ it('should toggle expandable section', () => __awaiter(void 0, void 0, void 0, function* () {
85
+ const code = 'const x = 5;\nconst y = 10;';
86
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { isExpandable: true, children: code }));
87
+ const toggleButton = react_1.screen.getByRole('button', { name: 'Show more' });
88
+ yield user_event_1.default.click(toggleButton);
89
+ expect(react_1.screen.getByRole('button', { name: 'Show less' })).toBeInTheDocument();
90
+ }));
91
+ it('should use custom expanded/collapsed text', () => {
92
+ const code = 'const x = 5;\nconst y = 10;';
93
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { isExpandable: true, expandedText: "Hide", collapsedText: "Reveal", children: code }));
94
+ expect(react_1.screen.getByRole('button', { name: 'Reveal' })).toBeInTheDocument();
95
+ });
96
+ it('should pass through expandableSectionProps', () => {
97
+ const code = 'const x = 5;\nconst y = 10;';
98
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { isExpandable: true, expandableSectionProps: { className: 'custom-expandable-class' }, children: code }));
99
+ const expandableSection = container.querySelector('.pf-v6-c-expandable-section.custom-expandable-class');
100
+ expect(expandableSection).toBeInTheDocument();
101
+ });
102
+ it('should render custom actions', () => {
103
+ const code = 'const x = 5;\nconst y = 10;';
104
+ const customAction = (0, jsx_runtime_1.jsx)("button", { "aria-label": "Custom action", children: "Custom" });
105
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { customActions: customAction, children: code }));
106
+ expect(react_1.screen.getByRole('button', { name: 'Custom action' })).toBeInTheDocument();
107
+ });
108
+ it('should apply isPrimary class to inline code', () => {
109
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { isPrimary: true, children: "const x = 5;" }));
110
+ const code = react_1.screen.getByText('const x = 5;');
111
+ expect(code).toHaveClass('pf-m-primary');
112
+ });
113
+ it('should apply shouldRetainStyles class to code block', () => {
114
+ const code = 'const x = 5;\nconst y = 10;';
115
+ const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { shouldRetainStyles: true, children: code }));
116
+ const codeBlockDiv = container.querySelector('.pf-chatbot__message-code-block');
117
+ expect(codeBlockDiv).toHaveClass('pf-m-markdown');
118
+ });
119
+ it('should use custom aria-label for copy button', () => {
120
+ const code = 'const x = 5;\nconst y = 10;';
121
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { "aria-label": "Copy this code", children: code }));
122
+ expect(react_1.screen.getByRole('button', { name: 'Copy this code' })).toBeInTheDocument();
123
+ });
124
+ it('should prioritize data-expanded-text over expandedText prop', () => {
125
+ const code = 'const x = 5;\nconst y = 10;';
126
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn());
127
+ (0, react_1.render)((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, { isExpandable: true, expandedText: "Custom Expanded", "data-expanded-text": "Data Expanded", children: code }));
128
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Message:', expect.stringContaining('data-expanded-text or data-collapsed-text will override'));
129
+ consoleErrorSpy.mockRestore();
130
+ });
131
+ });
@@ -46,8 +46,18 @@ const CodeBlockMessage = (_a) => {
46
46
  setIsExpanded(isExpanded);
47
47
  };
48
48
  // Handle clicking copy button
49
- const handleCopy = useCallback((event, text) => {
50
- navigator.clipboard.writeText(text.toString());
49
+ const handleCopy = useCallback((_event, text) => {
50
+ let textToCopy = '';
51
+ if (typeof text === 'string') {
52
+ textToCopy = text;
53
+ }
54
+ else {
55
+ if (codeBlockRef.current) {
56
+ const codeElement = codeBlockRef.current.querySelector('code');
57
+ textToCopy = (codeElement === null || codeElement === void 0 ? void 0 : codeElement.textContent) || '';
58
+ }
59
+ }
60
+ navigator.clipboard.writeText(textToCopy);
51
61
  setCopied(true);
52
62
  }, []);
53
63
  // Reset copied state
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1,126 @@
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, jsxs as _jsxs } from "react/jsx-runtime";
11
+ import '@testing-library/jest-dom';
12
+ import { render, screen } from '@testing-library/react';
13
+ import userEvent from '@testing-library/user-event';
14
+ import CodeBlockMessage from './CodeBlockMessage';
15
+ // Mock clipboard API
16
+ Object.assign(navigator, {
17
+ clipboard: {
18
+ writeText: jest.fn()
19
+ }
20
+ });
21
+ describe('CodeBlockMessage', () => {
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ });
25
+ it('should render inline code for single-line content', () => {
26
+ render(_jsx(CodeBlockMessage, { className: "language-javascript", children: "const x = 5;" }));
27
+ const code = screen.getByText('const x = 5;');
28
+ expect(code.tagName).toBe('CODE');
29
+ expect(code).toHaveClass('pf-chatbot__message-inline-code');
30
+ });
31
+ it('should render code block for multi-line content', () => {
32
+ const multilineCode = 'const x = 5;\nconst y = 10;';
33
+ const { container } = render(_jsx(CodeBlockMessage, { className: "language-javascript", children: multilineCode }));
34
+ const codeElement = container.querySelector('code');
35
+ expect(codeElement === null || codeElement === void 0 ? void 0 : codeElement.textContent).toBe(multilineCode);
36
+ });
37
+ it('should display language label', () => {
38
+ const code = 'const x = 5;\nconst y = 10;';
39
+ render(_jsx(CodeBlockMessage, { className: "language-javascript", children: code }));
40
+ expect(screen.getByText('javascript')).toBeInTheDocument();
41
+ });
42
+ it('should render copy button', () => {
43
+ const code = 'const x = 5;\nconst y = 10;';
44
+ render(_jsx(CodeBlockMessage, { children: code }));
45
+ expect(screen.getByRole('button', { name: 'Copy code' })).toBeInTheDocument();
46
+ });
47
+ it('should copy plain string content to clipboard', () => __awaiter(void 0, void 0, void 0, function* () {
48
+ const code = 'const x = 5;\nconst y = 10;';
49
+ render(_jsx(CodeBlockMessage, { children: code }));
50
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
51
+ yield userEvent.click(copyButton);
52
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(code);
53
+ }));
54
+ it('should extract text content from React elements when copying', () => __awaiter(void 0, void 0, void 0, function* () {
55
+ // Simulate what happens with syntax highlighting - children become React elements
56
+ const { container } = render(_jsxs(CodeBlockMessage, { className: "language-javascript", children: [_jsx("span", { className: "hljs-keyword", children: "const" }), " x = 5;", '\n', _jsx("span", { className: "hljs-keyword", children: "const" }), " y = 10;"] }));
57
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
58
+ yield userEvent.click(copyButton);
59
+ // Should extract actual text content from DOM, not "[object Object]"
60
+ const codeElement = container.querySelector('code');
61
+ const expectedText = (codeElement === null || codeElement === void 0 ? void 0 : codeElement.textContent) || '';
62
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedText);
63
+ expect(expectedText).not.toContain('[object Object]');
64
+ }));
65
+ it('should show check icon after copying', () => __awaiter(void 0, void 0, void 0, function* () {
66
+ const code = 'const x = 5;\nconst y = 10;';
67
+ render(_jsx(CodeBlockMessage, { children: code }));
68
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
69
+ yield userEvent.click(copyButton);
70
+ // Check icon should be visible (we can verify by checking if CopyIcon is not present)
71
+ const svgElement = copyButton.querySelector('svg');
72
+ expect(svgElement).toBeInTheDocument();
73
+ }));
74
+ it('should render expandable section when isExpandable is true', () => {
75
+ const code = 'const x = 5;\nconst y = 10;';
76
+ render(_jsx(CodeBlockMessage, { isExpandable: true, children: code }));
77
+ expect(screen.getByRole('button', { name: 'Show more' })).toBeInTheDocument();
78
+ });
79
+ it('should toggle expandable section', () => __awaiter(void 0, void 0, void 0, function* () {
80
+ const code = 'const x = 5;\nconst y = 10;';
81
+ render(_jsx(CodeBlockMessage, { isExpandable: true, children: code }));
82
+ const toggleButton = screen.getByRole('button', { name: 'Show more' });
83
+ yield userEvent.click(toggleButton);
84
+ expect(screen.getByRole('button', { name: 'Show less' })).toBeInTheDocument();
85
+ }));
86
+ it('should use custom expanded/collapsed text', () => {
87
+ const code = 'const x = 5;\nconst y = 10;';
88
+ render(_jsx(CodeBlockMessage, { isExpandable: true, expandedText: "Hide", collapsedText: "Reveal", children: code }));
89
+ expect(screen.getByRole('button', { name: 'Reveal' })).toBeInTheDocument();
90
+ });
91
+ it('should pass through expandableSectionProps', () => {
92
+ const code = 'const x = 5;\nconst y = 10;';
93
+ const { container } = render(_jsx(CodeBlockMessage, { isExpandable: true, expandableSectionProps: { className: 'custom-expandable-class' }, children: code }));
94
+ const expandableSection = container.querySelector('.pf-v6-c-expandable-section.custom-expandable-class');
95
+ expect(expandableSection).toBeInTheDocument();
96
+ });
97
+ it('should render custom actions', () => {
98
+ const code = 'const x = 5;\nconst y = 10;';
99
+ const customAction = _jsx("button", { "aria-label": "Custom action", children: "Custom" });
100
+ render(_jsx(CodeBlockMessage, { customActions: customAction, children: code }));
101
+ expect(screen.getByRole('button', { name: 'Custom action' })).toBeInTheDocument();
102
+ });
103
+ it('should apply isPrimary class to inline code', () => {
104
+ render(_jsx(CodeBlockMessage, { isPrimary: true, children: "const x = 5;" }));
105
+ const code = screen.getByText('const x = 5;');
106
+ expect(code).toHaveClass('pf-m-primary');
107
+ });
108
+ it('should apply shouldRetainStyles class to code block', () => {
109
+ const code = 'const x = 5;\nconst y = 10;';
110
+ const { container } = render(_jsx(CodeBlockMessage, { shouldRetainStyles: true, children: code }));
111
+ const codeBlockDiv = container.querySelector('.pf-chatbot__message-code-block');
112
+ expect(codeBlockDiv).toHaveClass('pf-m-markdown');
113
+ });
114
+ it('should use custom aria-label for copy button', () => {
115
+ const code = 'const x = 5;\nconst y = 10;';
116
+ render(_jsx(CodeBlockMessage, { "aria-label": "Copy this code", children: code }));
117
+ expect(screen.getByRole('button', { name: 'Copy this code' })).toBeInTheDocument();
118
+ });
119
+ it('should prioritize data-expanded-text over expandedText prop', () => {
120
+ const code = 'const x = 5;\nconst y = 10;';
121
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn());
122
+ render(_jsx(CodeBlockMessage, { isExpandable: true, expandedText: "Custom Expanded", "data-expanded-text": "Data Expanded", children: code }));
123
+ expect(consoleErrorSpy).toHaveBeenCalledWith('Message:', expect.stringContaining('data-expanded-text or data-collapsed-text will override'));
124
+ consoleErrorSpy.mockRestore();
125
+ });
126
+ });
@@ -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/DeepThinking/DeepThinking.test.tsx","../src/DeepThinking/DeepThinking.tsx","../src/DeepThinking/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/FilePreview/FilePreview.test.tsx","../src/FilePreview/FilePreview.tsx","../src/FilePreview/index.ts","../src/ImagePreview/ImagePreview.test.tsx","../src/ImagePreview/ImagePreview.tsx","../src/ImagePreview/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/MarkdownContent/MarkdownContent.test.tsx","../src/MarkdownContent/MarkdownContent.tsx","../src/MarkdownContent/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/SuperscriptMessage/SuperscriptMessage.tsx","../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/Onboarding/Onboarding.test.tsx","../src/Onboarding/Onboarding.tsx","../src/Onboarding/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/SourcesCardBase/SourcesCardBase.test.tsx","../src/SourcesCardBase/SourcesCardBase.tsx","../src/SourcesCardBase/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts","../src/ToolCall/ToolCall.test.tsx","../src/ToolCall/ToolCall.tsx","../src/ToolCall/index.ts","../src/ToolResponse/ToolResponse.test.tsx","../src/ToolResponse/ToolResponse.tsx","../src/ToolResponse/index.ts","../src/__mocks__/monaco-editor.ts","../src/__mocks__/rehype-external-links.ts","../src/__mocks__/rehype-highlight.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/DeepThinking/DeepThinking.test.tsx","../src/DeepThinking/DeepThinking.tsx","../src/DeepThinking/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/FilePreview/FilePreview.test.tsx","../src/FilePreview/FilePreview.tsx","../src/FilePreview/index.ts","../src/ImagePreview/ImagePreview.test.tsx","../src/ImagePreview/ImagePreview.tsx","../src/ImagePreview/index.ts","../src/LoadingMessage/LoadingMessage.test.tsx","../src/LoadingMessage/LoadingMessage.tsx","../src/LoadingMessage/index.ts","../src/MarkdownContent/MarkdownContent.test.tsx","../src/MarkdownContent/MarkdownContent.tsx","../src/MarkdownContent/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.test.tsx","../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/SuperscriptMessage/SuperscriptMessage.tsx","../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/Onboarding/Onboarding.test.tsx","../src/Onboarding/Onboarding.tsx","../src/Onboarding/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/SourcesCardBase/SourcesCardBase.test.tsx","../src/SourcesCardBase/SourcesCardBase.tsx","../src/SourcesCardBase/index.ts","../src/TermsOfUse/TermsOfUse.test.tsx","../src/TermsOfUse/TermsOfUse.tsx","../src/TermsOfUse/index.ts","../src/ToolCall/ToolCall.test.tsx","../src/ToolCall/ToolCall.tsx","../src/ToolCall/index.ts","../src/ToolResponse/ToolResponse.test.tsx","../src/ToolResponse/ToolResponse.tsx","../src/ToolResponse/index.ts","../src/__mocks__/monaco-editor.ts","../src/__mocks__/rehype-external-links.ts","../src/__mocks__/rehype-highlight.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.5.0-prerelease.24",
3
+ "version": "6.5.0-prerelease.25",
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",
@@ -0,0 +1,171 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import CodeBlockMessage from './CodeBlockMessage';
5
+
6
+ // Mock clipboard API
7
+ Object.assign(navigator, {
8
+ clipboard: {
9
+ writeText: jest.fn()
10
+ }
11
+ });
12
+
13
+ describe('CodeBlockMessage', () => {
14
+ beforeEach(() => {
15
+ jest.clearAllMocks();
16
+ });
17
+
18
+ it('should render inline code for single-line content', () => {
19
+ render(<CodeBlockMessage className="language-javascript">const x = 5;</CodeBlockMessage>);
20
+ const code = screen.getByText('const x = 5;');
21
+ expect(code.tagName).toBe('CODE');
22
+ expect(code).toHaveClass('pf-chatbot__message-inline-code');
23
+ });
24
+
25
+ it('should render code block for multi-line content', () => {
26
+ const multilineCode = 'const x = 5;\nconst y = 10;';
27
+ const { container } = render(<CodeBlockMessage className="language-javascript">{multilineCode}</CodeBlockMessage>);
28
+ const codeElement = container.querySelector('code');
29
+ expect(codeElement?.textContent).toBe(multilineCode);
30
+ });
31
+
32
+ it('should display language label', () => {
33
+ const code = 'const x = 5;\nconst y = 10;';
34
+ render(<CodeBlockMessage className="language-javascript">{code}</CodeBlockMessage>);
35
+ expect(screen.getByText('javascript')).toBeInTheDocument();
36
+ });
37
+
38
+ it('should render copy button', () => {
39
+ const code = 'const x = 5;\nconst y = 10;';
40
+ render(<CodeBlockMessage>{code}</CodeBlockMessage>);
41
+ expect(screen.getByRole('button', { name: 'Copy code' })).toBeInTheDocument();
42
+ });
43
+
44
+ it('should copy plain string content to clipboard', async () => {
45
+ const code = 'const x = 5;\nconst y = 10;';
46
+ render(<CodeBlockMessage>{code}</CodeBlockMessage>);
47
+
48
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
49
+ await userEvent.click(copyButton);
50
+
51
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(code);
52
+ });
53
+
54
+ it('should extract text content from React elements when copying', async () => {
55
+ // Simulate what happens with syntax highlighting - children become React elements
56
+ const { container } = render(
57
+ <CodeBlockMessage className="language-javascript">
58
+ <span className="hljs-keyword">const</span> x = 5;{'\n'}
59
+ <span className="hljs-keyword">const</span> y = 10;
60
+ </CodeBlockMessage>
61
+ );
62
+
63
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
64
+ await userEvent.click(copyButton);
65
+
66
+ // Should extract actual text content from DOM, not "[object Object]"
67
+ const codeElement = container.querySelector('code');
68
+ const expectedText = codeElement?.textContent || '';
69
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedText);
70
+ expect(expectedText).not.toContain('[object Object]');
71
+ });
72
+
73
+ it('should show check icon after copying', async () => {
74
+ const code = 'const x = 5;\nconst y = 10;';
75
+ render(<CodeBlockMessage>{code}</CodeBlockMessage>);
76
+
77
+ const copyButton = screen.getByRole('button', { name: 'Copy code' });
78
+ await userEvent.click(copyButton);
79
+
80
+ // Check icon should be visible (we can verify by checking if CopyIcon is not present)
81
+ const svgElement = copyButton.querySelector('svg');
82
+ expect(svgElement).toBeInTheDocument();
83
+ });
84
+
85
+ it('should render expandable section when isExpandable is true', () => {
86
+ const code = 'const x = 5;\nconst y = 10;';
87
+ render(<CodeBlockMessage isExpandable>{code}</CodeBlockMessage>);
88
+
89
+ expect(screen.getByRole('button', { name: 'Show more' })).toBeInTheDocument();
90
+ });
91
+
92
+ it('should toggle expandable section', async () => {
93
+ const code = 'const x = 5;\nconst y = 10;';
94
+ render(<CodeBlockMessage isExpandable>{code}</CodeBlockMessage>);
95
+
96
+ const toggleButton = screen.getByRole('button', { name: 'Show more' });
97
+ await userEvent.click(toggleButton);
98
+
99
+ expect(screen.getByRole('button', { name: 'Show less' })).toBeInTheDocument();
100
+ });
101
+
102
+ it('should use custom expanded/collapsed text', () => {
103
+ const code = 'const x = 5;\nconst y = 10;';
104
+ render(
105
+ <CodeBlockMessage isExpandable expandedText="Hide" collapsedText="Reveal">
106
+ {code}
107
+ </CodeBlockMessage>
108
+ );
109
+
110
+ expect(screen.getByRole('button', { name: 'Reveal' })).toBeInTheDocument();
111
+ });
112
+
113
+ it('should pass through expandableSectionProps', () => {
114
+ const code = 'const x = 5;\nconst y = 10;';
115
+ const { container } = render(
116
+ <CodeBlockMessage isExpandable expandableSectionProps={{ className: 'custom-expandable-class' }}>
117
+ {code}
118
+ </CodeBlockMessage>
119
+ );
120
+
121
+ const expandableSection = container.querySelector('.pf-v6-c-expandable-section.custom-expandable-class');
122
+ expect(expandableSection).toBeInTheDocument();
123
+ });
124
+
125
+ it('should render custom actions', () => {
126
+ const code = 'const x = 5;\nconst y = 10;';
127
+ const customAction = <button aria-label="Custom action">Custom</button>;
128
+ render(<CodeBlockMessage customActions={customAction}>{code}</CodeBlockMessage>);
129
+
130
+ expect(screen.getByRole('button', { name: 'Custom action' })).toBeInTheDocument();
131
+ });
132
+
133
+ it('should apply isPrimary class to inline code', () => {
134
+ render(<CodeBlockMessage isPrimary>const x = 5;</CodeBlockMessage>);
135
+ const code = screen.getByText('const x = 5;');
136
+ expect(code).toHaveClass('pf-m-primary');
137
+ });
138
+
139
+ it('should apply shouldRetainStyles class to code block', () => {
140
+ const code = 'const x = 5;\nconst y = 10;';
141
+ const { container } = render(<CodeBlockMessage shouldRetainStyles>{code}</CodeBlockMessage>);
142
+
143
+ const codeBlockDiv = container.querySelector('.pf-chatbot__message-code-block');
144
+ expect(codeBlockDiv).toHaveClass('pf-m-markdown');
145
+ });
146
+
147
+ it('should use custom aria-label for copy button', () => {
148
+ const code = 'const x = 5;\nconst y = 10;';
149
+ render(<CodeBlockMessage aria-label="Copy this code">{code}</CodeBlockMessage>);
150
+
151
+ expect(screen.getByRole('button', { name: 'Copy this code' })).toBeInTheDocument();
152
+ });
153
+
154
+ it('should prioritize data-expanded-text over expandedText prop', () => {
155
+ const code = 'const x = 5;\nconst y = 10;';
156
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn());
157
+
158
+ render(
159
+ <CodeBlockMessage isExpandable expandedText="Custom Expanded" data-expanded-text="Data Expanded">
160
+ {code}
161
+ </CodeBlockMessage>
162
+ );
163
+
164
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
165
+ 'Message:',
166
+ expect.stringContaining('data-expanded-text or data-collapsed-text will override')
167
+ );
168
+
169
+ consoleErrorSpy.mockRestore();
170
+ });
171
+ });
@@ -92,13 +92,22 @@ const CodeBlockMessage = ({
92
92
  );
93
93
  }
94
94
 
95
- const onToggle = (isExpanded) => {
95
+ const onToggle = (isExpanded: boolean) => {
96
96
  setIsExpanded(isExpanded);
97
97
  };
98
98
 
99
99
  // Handle clicking copy button
100
- const handleCopy = useCallback((event, text) => {
101
- navigator.clipboard.writeText(text.toString());
100
+ const handleCopy = useCallback((_event: React.MouseEvent, text: React.ReactNode) => {
101
+ let textToCopy = '';
102
+ if (typeof text === 'string') {
103
+ textToCopy = text;
104
+ } else {
105
+ if (codeBlockRef.current) {
106
+ const codeElement = codeBlockRef.current.querySelector('code');
107
+ textToCopy = codeElement?.textContent || '';
108
+ }
109
+ }
110
+ navigator.clipboard.writeText(textToCopy);
102
111
  setCopied(true);
103
112
  }, []);
104
113