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

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 (23) hide show
  1. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +9 -1
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +9 -2
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +38 -0
  4. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +12 -2
  5. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.test.d.ts +1 -0
  6. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.test.js +131 -0
  7. package/dist/css/main.css +11 -0
  8. package/dist/css/main.css.map +1 -1
  9. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +9 -1
  10. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +10 -3
  11. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +38 -0
  12. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +12 -2
  13. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.test.d.ts +1 -0
  14. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.test.js +126 -0
  15. package/dist/tsconfig.tsbuildinfo +1 -1
  16. package/package.json +1 -1
  17. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSearchActions.tsx +198 -0
  18. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +12 -2
  19. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +18 -0
  20. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +95 -0
  21. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +51 -15
  22. package/src/Message/CodeBlockMessage/CodeBlockMessage.test.tsx +171 -0
  23. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +12 -3
@@ -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.26",
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,198 @@
1
+ import { FunctionComponent, useState } from 'react';
2
+ import { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
3
+ import ChatbotConversationHistoryNav, {
4
+ Conversation
5
+ } from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
6
+ import {
7
+ Button,
8
+ Checkbox,
9
+ MenuToggle,
10
+ MenuToggleElement,
11
+ Select,
12
+ SelectList,
13
+ SelectOption,
14
+ Tooltip
15
+ } from '@patternfly/react-core';
16
+ import { FilterIcon, SortAmountDownIcon } from '@patternfly/react-icons';
17
+
18
+ const initialConversations: { [key: string]: Conversation[] } = {
19
+ Today: [{ id: '1', text: 'Red Hat products and services' }],
20
+ 'This month': [
21
+ {
22
+ id: '2',
23
+ text: 'Enterprise Linux installation and setup'
24
+ },
25
+ { id: '3', text: 'Troubleshoot system crash' }
26
+ ],
27
+ March: [
28
+ { id: '4', text: 'Ansible security and updates' },
29
+ { id: '5', text: 'Red Hat certification' },
30
+ { id: '6', text: 'Lightspeed user documentation' }
31
+ ],
32
+ February: [
33
+ { id: '7', text: 'Crashing pod assistance' },
34
+ { id: '8', text: 'OpenShift AI pipelines' },
35
+ { id: '9', text: 'Updating subscription plan' },
36
+ { id: '10', text: 'Red Hat licensing options' }
37
+ ],
38
+ January: [
39
+ { id: '11', text: 'RHEL system performance' },
40
+ { id: '12', text: 'Manage user accounts' }
41
+ ]
42
+ };
43
+
44
+ export const ChatbotHeaderTitleDemo: FunctionComponent = () => {
45
+ const [isDrawerOpen, setIsDrawerOpen] = useState(true);
46
+ const [hasDrawerHeadDivider, setHasDrawerHeadDivider] = useState(false);
47
+ const [showSearchActionStart, setShowSearchActionStart] = useState(false);
48
+ const [showSearchActionEnd, setShowSearchActionEnd] = useState(false);
49
+ const [isLoading, setIsLoading] = useState(false);
50
+ const [isSortSelectOpen, setIsSortSelectOpen] = useState(false);
51
+ const [selectedSort, setSelectedSort] = useState<string>('newest');
52
+ const [conversations, setConversations] = useState<Conversation[] | { [key: string]: Conversation[] }>(
53
+ initialConversations
54
+ );
55
+ const displayMode = ChatbotDisplayMode.embedded;
56
+
57
+ const sortLabels: { [key: string]: string } = {
58
+ newest: 'Date (newest first)',
59
+ oldest: 'Date (oldest first)',
60
+ 'alphabetical-asc': 'Name (A-Z)',
61
+ 'alphabetical-desc': 'Name (Z-A)'
62
+ };
63
+
64
+ const onSortSelect = (
65
+ _event: React.MouseEvent<Element, MouseEvent> | undefined,
66
+ value: string | number | undefined
67
+ ) => {
68
+ setSelectedSort(value as string);
69
+ setIsSortSelectOpen(false);
70
+ };
71
+
72
+ const findMatchingItems = (targetValue: string) => {
73
+ const filteredConversations = Object.entries(initialConversations).reduce((acc, [key, items]) => {
74
+ const filteredItems = items.filter((item) => item.text.toLowerCase().includes(targetValue.toLowerCase()));
75
+ if (filteredItems.length > 0) {
76
+ acc[key] = filteredItems;
77
+ }
78
+ return acc;
79
+ }, {});
80
+
81
+ return filteredConversations;
82
+ };
83
+
84
+ return (
85
+ <>
86
+ <Checkbox
87
+ label="Display drawer"
88
+ isChecked={isDrawerOpen}
89
+ onChange={() => setIsDrawerOpen(!isDrawerOpen)}
90
+ id="search-actions-drawer-visible"
91
+ name="drawer-visible"
92
+ />
93
+ <Checkbox
94
+ label="Show drawer head divider"
95
+ isChecked={hasDrawerHeadDivider}
96
+ onChange={() => setHasDrawerHeadDivider(!hasDrawerHeadDivider)}
97
+ id="search-actions-drawer-head-divider"
98
+ name="drawer-head-divider"
99
+ />
100
+ <Checkbox
101
+ label="Show search action start"
102
+ isChecked={showSearchActionStart}
103
+ onChange={() => setShowSearchActionStart(!showSearchActionStart)}
104
+ id="search-actions-show-search-action-start"
105
+ name="show-search-action-start"
106
+ />
107
+ <Checkbox
108
+ label="Show search action end"
109
+ isChecked={showSearchActionEnd}
110
+ onChange={() => setShowSearchActionEnd(!showSearchActionEnd)}
111
+ id="search-actions-show-search-action-end"
112
+ name="show-search-action-end"
113
+ />
114
+ <Checkbox
115
+ label="Show loading state"
116
+ isChecked={isLoading}
117
+ onChange={() => setIsLoading(!isLoading)}
118
+ id="search-actions-drawer-is-loading"
119
+ name="drawer-is-loading"
120
+ />
121
+ <ChatbotConversationHistoryNav
122
+ displayMode={displayMode}
123
+ onDrawerToggle={() => setIsDrawerOpen(!isDrawerOpen)}
124
+ isDrawerOpen={isDrawerOpen}
125
+ setIsDrawerOpen={setIsDrawerOpen}
126
+ // eslint-disable-next-line no-console
127
+ onSelectActiveItem={(e, selectedItem) => console.log(`Selected history item with id ${selectedItem}`)}
128
+ conversations={conversations}
129
+ onNewChat={() => {
130
+ setIsDrawerOpen(!isDrawerOpen);
131
+ }}
132
+ handleTextInputChange={(value: string) => {
133
+ if (value === '') {
134
+ setConversations(initialConversations);
135
+ } else {
136
+ const newConversations: { [key: string]: Conversation[] } = findMatchingItems(value);
137
+ setConversations(newConversations);
138
+ }
139
+ }}
140
+ drawerContent={<div>Drawer content</div>}
141
+ hasDrawerHeadDivider={hasDrawerHeadDivider}
142
+ isLoading={isLoading}
143
+ searchActionStart={
144
+ showSearchActionStart ? (
145
+ <Tooltip content="Filter options" aria="none" aria-live="off">
146
+ <Button
147
+ variant="control"
148
+ aria-label="Filter options"
149
+ // eslint-disable-next-line no-console
150
+ onClick={() => console.log('Filter button clicked')}
151
+ icon={<FilterIcon />}
152
+ />
153
+ </Tooltip>
154
+ ) : undefined
155
+ }
156
+ searchActionEnd={
157
+ showSearchActionEnd ? (
158
+ <Select
159
+ id="sort-select"
160
+ isOpen={isSortSelectOpen}
161
+ selected={selectedSort}
162
+ onSelect={onSortSelect}
163
+ shouldFocusToggleOnSelect
164
+ onOpenChange={(isOpen) => setIsSortSelectOpen(isOpen)}
165
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
166
+ <Tooltip aria="none" aria-live="off" content={`Sort - ${sortLabels[selectedSort]}`}>
167
+ <MenuToggle
168
+ ref={toggleRef}
169
+ onClick={() => setIsSortSelectOpen(!isSortSelectOpen)}
170
+ isExpanded={isSortSelectOpen}
171
+ variant="plain"
172
+ aria-label={`${sortLabels[selectedSort]}, Sort conversations`}
173
+ icon={
174
+ <SortAmountDownIcon
175
+ style={{
176
+ transform:
177
+ selectedSort === 'oldest' || selectedSort === 'alphabetical-asc' ? 'scaleY(-1)' : 'none'
178
+ }}
179
+ />
180
+ }
181
+ />
182
+ </Tooltip>
183
+ )}
184
+ >
185
+ <SelectList>
186
+ {Object.keys(sortLabels).map((currentLabel) => (
187
+ <SelectOption key={currentLabel} value={currentLabel}>
188
+ {sortLabels[currentLabel]}
189
+ </SelectOption>
190
+ ))}
191
+ </SelectList>
192
+ </Select>
193
+ ) : undefined
194
+ }
195
+ />
196
+ </>
197
+ );
198
+ };
@@ -74,7 +74,7 @@ import { BellIcon, CalendarAltIcon, ClipboardIcon, CodeIcon, ThumbtackIcon, Uplo
74
74
  import { useDropzone } from 'react-dropzone';
75
75
 
76
76
  import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
77
- import { DropdownItem, DropdownList, Checkbox } from '@patternfly/react-core';
77
+ import { Button, DropdownItem, DropdownList, Checkbox, MenuToggle, Select, SelectList, SelectOption } from '@patternfly/react-core';
78
78
 
79
79
  import OutlinedWindowRestoreIcon from '@patternfly/react-icons/dist/esm/icons/outlined-window-restore-icon';
80
80
  import ExpandIcon from '@patternfly/react-icons/dist/esm/icons/expand-icon';
@@ -87,7 +87,7 @@ import userAvatar from '../Messages/user_avatar.svg';
87
87
  import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
88
88
  import termsAndConditionsHeader from './PF-TermsAndConditionsHeader.svg';
89
89
  import onboardingHeader from './RH-Hat-Image.svg';
90
- import { CloseIcon, SearchIcon, OutlinedCommentsIcon } from '@patternfly/react-icons';
90
+ import { CloseIcon, SearchIcon, OutlinedCommentsIcon, FilterIcon, SortAmountDownIcon } from '@patternfly/react-icons';
91
91
  import { FunctionComponent, FormEvent, useState, useRef, MouseEvent, isValidElement, cloneElement, Children, ReactNode, Ref, MouseEvent as ReactMouseEvent, CSSProperties, useEffect} from 'react';
92
92
  import FilePreview from '@patternfly/chatbot/dist/dynamic/FilePreview';
93
93
 
@@ -371,6 +371,16 @@ Both the search input field and "New chat" buttons are optional. The `reverseBut
371
371
 
372
372
  ```
373
373
 
374
+ ### Drawer with search actions
375
+
376
+ You can customize the search experience within the conversation history drawer via the `searchActionStart` and `searchActionEnd` props, which provide additional search controls before and after the input field. These props are useful for adding filtering, sorting, or other search-related functionality.
377
+
378
+ You can also add a visual divider between the drawer head and the title by setting `hasDrawerHeadDivider` to `true`.
379
+
380
+ ```ts file="./ChatbotHeaderDrawerWithSearchActions.tsx"
381
+
382
+ ```
383
+
374
384
  ### Drawer with conversation actions
375
385
 
376
386
  Actions can be added to conversations with `menuItems`. Optionally, you can also add a `className` to the menu via `menuClassName`, change the default aria-label and tooltip content via `label`, or add an `onSelect` callback for when a user selects an item.
@@ -7,6 +7,11 @@
7
7
  border-radius: var(--pf-t--global--border--radius--medium);
8
8
  }
9
9
 
10
+ .pf-chatbot__heading-divider {
11
+ padding-inline-start: var(--pf-t--global--spacer--lg);
12
+ padding-inline-end: var(--pf-t--global--spacer--lg);
13
+ }
14
+
10
15
  // Drawer title
11
16
  // ----------------------------------------------------------------------------
12
17
  .pf-chatbot__heading-container {
@@ -28,6 +33,19 @@
28
33
  justify-content: flex-start;
29
34
  gap: var(--pf-t--global--spacer--gap--text-to-element--default);
30
35
  }
36
+
37
+ // Drawer search and actions
38
+ .pf-chatbot__history-search-actions {
39
+ .pf-v6-c-button.pf-m-control {
40
+ --pf-v6-c-button--m-control--PaddingInlineStart: var(--pf-t--global--spacer--control--horizontal--compact);
41
+ --pf-v6-c-button--m-control--PaddingInlineEnd: var(--pf-t--global--spacer--control--horizontal--compact);
42
+ }
43
+ }
44
+
45
+ .pf-chatbot__input {
46
+ width: 100%;
47
+ }
48
+
31
49
  // Drawer menu
32
50
  // ----------------------------------------------------------------------------
33
51
  .pf-v6-c-menu {
@@ -592,6 +592,101 @@ describe('ChatbotConversationHistoryNav', () => {
592
592
  expect(screen.getByRole('dialog', { name: /Chat history I am a sample search/i })).toBeInTheDocument();
593
593
  });
594
594
 
595
+ it('Does not render search actions by default', () => {
596
+ const handleSearch = jest.fn();
597
+ const groupedConversations: { [key: string]: Conversation[] } = {
598
+ Today: [...initialConversations, { id: '2', text: 'Chatbot extension' }]
599
+ };
600
+
601
+ render(
602
+ <ChatbotConversationHistoryNav
603
+ onDrawerToggle={onDrawerToggle}
604
+ isDrawerOpen={true}
605
+ displayMode={ChatbotDisplayMode.fullscreen}
606
+ setIsDrawerOpen={jest.fn()}
607
+ reverseButtonOrder={false}
608
+ conversations={groupedConversations}
609
+ handleTextInputChange={handleSearch}
610
+ />
611
+ );
612
+
613
+ const searchInput = screen.getByPlaceholderText(/Search/i);
614
+
615
+ expect(searchInput.parentElement?.previousElementSibling).toBeNull();
616
+ expect(searchInput.parentElement?.nextElementSibling).toBeNull();
617
+ });
618
+
619
+ it('Renders with action at start when searchActionStart is passed', () => {
620
+ const handleSearch = jest.fn();
621
+ const groupedConversations: { [key: string]: Conversation[] } = {
622
+ Today: [...initialConversations, { id: '2', text: 'Chatbot extension' }]
623
+ };
624
+
625
+ render(
626
+ <ChatbotConversationHistoryNav
627
+ onDrawerToggle={onDrawerToggle}
628
+ isDrawerOpen={true}
629
+ displayMode={ChatbotDisplayMode.fullscreen}
630
+ setIsDrawerOpen={jest.fn()}
631
+ reverseButtonOrder={false}
632
+ conversations={groupedConversations}
633
+ handleTextInputChange={handleSearch}
634
+ searchActionStart={<div>Search action start test</div>}
635
+ />
636
+ );
637
+
638
+ expect(screen.getByText('Search action start test')).toBeVisible();
639
+ });
640
+
641
+ it('Renders with action at end when searchActionEnd is passed', () => {
642
+ const handleSearch = jest.fn();
643
+ const groupedConversations: { [key: string]: Conversation[] } = {
644
+ Today: [...initialConversations, { id: '2', text: 'Chatbot extension' }]
645
+ };
646
+
647
+ render(
648
+ <ChatbotConversationHistoryNav
649
+ onDrawerToggle={onDrawerToggle}
650
+ isDrawerOpen={true}
651
+ displayMode={ChatbotDisplayMode.fullscreen}
652
+ setIsDrawerOpen={jest.fn()}
653
+ reverseButtonOrder={false}
654
+ handleTextInputChange={handleSearch}
655
+ conversations={groupedConversations}
656
+ searchActionEnd={<div>Search action end test</div>}
657
+ />
658
+ );
659
+
660
+ expect(screen.getByText('Search action end test')).toBeVisible();
661
+ });
662
+
663
+ it('Overrides default search input and actions when searchToolbar is passed', () => {
664
+ const handleSearch = jest.fn();
665
+ const groupedConversations: { [key: string]: Conversation[] } = {
666
+ Today: [...initialConversations, { id: '2', text: 'Chatbot extension' }]
667
+ };
668
+
669
+ render(
670
+ <ChatbotConversationHistoryNav
671
+ onDrawerToggle={onDrawerToggle}
672
+ isDrawerOpen={true}
673
+ displayMode={ChatbotDisplayMode.fullscreen}
674
+ setIsDrawerOpen={jest.fn()}
675
+ reverseButtonOrder={false}
676
+ conversations={groupedConversations}
677
+ handleTextInputChange={handleSearch}
678
+ searchActionStart={<div>Search action start test</div>}
679
+ searchActionEnd={<div>Search action end test</div>}
680
+ searchToolbar={<div>Custom toolbar</div>}
681
+ />
682
+ );
683
+
684
+ expect(screen.queryByPlaceholderText(/Search/i)).not.toBeInTheDocument();
685
+ expect(screen.queryByText('Search action start test')).not.toBeInTheDocument();
686
+ expect(screen.queryByText('Search action end test')).not.toBeInTheDocument();
687
+ expect(screen.getByText('Custom toolbar')).toBeInTheDocument();
688
+ });
689
+
595
690
  it('overrides nav title heading level when navTitleProps.headingLevel is passed', () => {
596
691
  render(
597
692
  <ChatbotConversationHistoryNav