@patternfly/chatbot 6.4.0-prerelease.2 → 6.4.0-prerelease.21
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.
- package/dist/cjs/Chatbot/Chatbot.js +1 -7
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +15 -9
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +40 -2
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +25 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +22 -0
- package/dist/cjs/ChatbotHeader/index.d.ts +1 -0
- package/dist/cjs/ChatbotHeader/index.js +1 -0
- package/dist/cjs/DeepThinking/DeepThinking.d.ts +18 -0
- package/dist/cjs/DeepThinking/DeepThinking.js +18 -0
- package/dist/cjs/DeepThinking/DeepThinking.test.d.ts +1 -0
- package/dist/cjs/DeepThinking/DeepThinking.test.js +48 -0
- package/dist/cjs/DeepThinking/index.d.ts +2 -0
- package/dist/cjs/DeepThinking/index.js +23 -0
- package/dist/cjs/FileDetails/FileDetails.d.ts +22 -3
- package/dist/cjs/FileDetails/FileDetails.js +27 -912
- package/dist/cjs/FileDetails/FileDetails.test.js +16 -0
- package/dist/cjs/FileDetailsLabel/FileDetailsLabel.d.ts +8 -2
- package/dist/cjs/FileDetailsLabel/FileDetailsLabel.js +14 -2
- package/dist/cjs/FileDetailsLabel/FileDetailsLabel.test.js +19 -1
- package/dist/cjs/FilePreview/FilePreview.d.ts +26 -0
- package/dist/cjs/FilePreview/FilePreview.js +26 -0
- package/dist/cjs/FilePreview/FilePreview.test.d.ts +1 -0
- package/dist/cjs/FilePreview/FilePreview.test.js +97 -0
- package/dist/cjs/FilePreview/index.d.ts +2 -0
- package/dist/cjs/FilePreview/index.js +23 -0
- package/dist/cjs/ImagePreview/ImagePreview.d.ts +53 -0
- package/dist/cjs/ImagePreview/ImagePreview.js +47 -0
- package/dist/cjs/ImagePreview/ImagePreview.test.d.ts +1 -0
- package/dist/cjs/ImagePreview/ImagePreview.test.js +225 -0
- package/dist/cjs/ImagePreview/index.d.ts +2 -0
- package/dist/cjs/ImagePreview/index.js +23 -0
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +3 -3
- package/dist/cjs/Message/LinkMessage/LinkMessage.d.ts +2 -1
- package/dist/cjs/Message/LinkMessage/LinkMessage.js +7 -3
- package/dist/cjs/Message/ListMessage/ListItemMessage.d.ts +1 -1
- package/dist/cjs/Message/ListMessage/ListItemMessage.js +16 -1
- package/dist/cjs/Message/Message.d.ts +15 -0
- package/dist/cjs/Message/Message.js +129 -32
- package/dist/cjs/Message/Message.test.js +71 -0
- package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
- package/dist/cjs/Message/SuperscriptMessage/SuperscriptMessage.js +5 -0
- package/dist/cjs/Message/UserFeedback/UserFeedback.d.ts +15 -1
- package/dist/cjs/Message/UserFeedback/UserFeedback.js +4 -4
- package/dist/cjs/Message/UserFeedback/UserFeedback.test.js +44 -0
- package/dist/cjs/MessageBar/MessageBar.js +19 -4
- package/dist/cjs/MessageBox/JumpButton.d.ts +5 -0
- package/dist/cjs/MessageBox/JumpButton.js +1 -1
- package/dist/cjs/MessageBox/JumpButton.test.js +4 -4
- package/dist/cjs/MessageBox/MessageBox.d.ts +9 -0
- package/dist/cjs/MessageBox/MessageBox.js +2 -2
- package/dist/cjs/MessageBox/MessageBox.test.js +2 -2
- package/dist/cjs/SourcesCard/SourcesCard.d.ts +13 -1
- package/dist/cjs/SourcesCard/SourcesCard.js +6 -6
- package/dist/cjs/SourcesCard/SourcesCard.test.js +49 -0
- package/dist/cjs/ToolResponse/ToolResponse.d.ts +30 -0
- package/dist/cjs/ToolResponse/ToolResponse.js +18 -0
- package/dist/cjs/ToolResponse/ToolResponse.test.d.ts +1 -0
- package/dist/cjs/ToolResponse/ToolResponse.test.js +60 -0
- package/dist/cjs/ToolResponse/index.d.ts +2 -0
- package/dist/cjs/ToolResponse/index.js +23 -0
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.js +13 -1
- package/dist/css/main.css +339 -27
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/DeepThinking/package.json +1 -0
- package/dist/dynamic/FilePreview/package.json +1 -0
- package/dist/dynamic/ImagePreview/package.json +1 -0
- package/dist/dynamic/ToolResponse/package.json +1 -0
- package/dist/esm/Chatbot/Chatbot.js +1 -7
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +22 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +17 -11
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +41 -3
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.test.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.d.ts +18 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +22 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.d.ts +1 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.test.js +20 -0
- package/dist/esm/ChatbotHeader/index.d.ts +1 -0
- package/dist/esm/ChatbotHeader/index.js +1 -0
- package/dist/esm/DeepThinking/DeepThinking.d.ts +18 -0
- package/dist/esm/DeepThinking/DeepThinking.js +14 -0
- package/dist/esm/DeepThinking/DeepThinking.test.d.ts +1 -0
- package/dist/esm/DeepThinking/DeepThinking.test.js +43 -0
- package/dist/esm/DeepThinking/index.d.ts +2 -0
- package/dist/esm/DeepThinking/index.js +2 -0
- package/dist/esm/FileDetails/FileDetails.d.ts +22 -3
- package/dist/esm/FileDetails/FileDetails.js +27 -912
- package/dist/esm/FileDetails/FileDetails.test.js +16 -0
- package/dist/esm/FileDetailsLabel/FileDetailsLabel.d.ts +8 -2
- package/dist/esm/FileDetailsLabel/FileDetailsLabel.js +14 -2
- package/dist/esm/FileDetailsLabel/FileDetailsLabel.test.js +19 -1
- package/dist/esm/FilePreview/FilePreview.d.ts +26 -0
- package/dist/esm/FilePreview/FilePreview.js +21 -0
- package/dist/esm/FilePreview/FilePreview.test.d.ts +1 -0
- package/dist/esm/FilePreview/FilePreview.test.js +92 -0
- package/dist/esm/FilePreview/index.d.ts +2 -0
- package/dist/esm/FilePreview/index.js +2 -0
- package/dist/esm/ImagePreview/ImagePreview.d.ts +53 -0
- package/dist/esm/ImagePreview/ImagePreview.js +42 -0
- package/dist/esm/ImagePreview/ImagePreview.test.d.ts +1 -0
- package/dist/esm/ImagePreview/ImagePreview.test.js +220 -0
- package/dist/esm/ImagePreview/index.d.ts +2 -0
- package/dist/esm/ImagePreview/index.js +2 -0
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +5 -5
- package/dist/esm/Message/LinkMessage/LinkMessage.d.ts +2 -1
- package/dist/esm/Message/LinkMessage/LinkMessage.js +7 -3
- package/dist/esm/Message/ListMessage/ListItemMessage.d.ts +1 -1
- package/dist/esm/Message/ListMessage/ListItemMessage.js +16 -1
- package/dist/esm/Message/Message.d.ts +15 -0
- package/dist/esm/Message/Message.js +129 -32
- package/dist/esm/Message/Message.test.js +71 -0
- package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.d.ts +3 -0
- package/dist/esm/Message/SuperscriptMessage/SuperscriptMessage.js +3 -0
- package/dist/esm/Message/UserFeedback/UserFeedback.d.ts +15 -1
- package/dist/esm/Message/UserFeedback/UserFeedback.js +4 -4
- package/dist/esm/Message/UserFeedback/UserFeedback.test.js +45 -1
- package/dist/esm/MessageBar/MessageBar.js +19 -4
- package/dist/esm/MessageBox/JumpButton.d.ts +5 -0
- package/dist/esm/MessageBox/JumpButton.js +1 -1
- package/dist/esm/MessageBox/JumpButton.test.js +4 -4
- package/dist/esm/MessageBox/MessageBox.d.ts +9 -0
- package/dist/esm/MessageBox/MessageBox.js +2 -2
- package/dist/esm/MessageBox/MessageBox.test.js +2 -2
- package/dist/esm/SourcesCard/SourcesCard.d.ts +13 -1
- package/dist/esm/SourcesCard/SourcesCard.js +6 -6
- package/dist/esm/SourcesCard/SourcesCard.test.js +50 -1
- package/dist/esm/ToolResponse/ToolResponse.d.ts +30 -0
- package/dist/esm/ToolResponse/ToolResponse.js +14 -0
- package/dist/esm/ToolResponse/ToolResponse.test.d.ts +1 -0
- package/dist/esm/ToolResponse/ToolResponse.test.js +55 -0
- package/dist/esm/ToolResponse/index.d.ts +2 -0
- package/dist/esm/ToolResponse/index.js +2 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.js +8 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/AttachmentEdit.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +101 -3
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/FilePreview.tsx +33 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/ImagePreview.tsx +53 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDeepThinking.tsx +17 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithFeedback.tsx +111 -85
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +70 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +135 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +38 -4
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/PreviewAttachment.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +107 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +616 -3
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/file-preview.svg +9 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotConversationEditing.tsx +202 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +17 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +36 -5
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +12 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +22 -3
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +1 -1
- package/patternfly-docs/patternfly-docs.config.js +1 -1
- package/src/Chatbot/Chatbot.scss +9 -2
- package/src/Chatbot/Chatbot.tsx +18 -31
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +16 -10
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +132 -3
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +80 -33
- package/src/ChatbotHeader/ChatbotHeaderMenu.test.tsx +1 -1
- package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.test.tsx +25 -0
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +64 -0
- package/src/ChatbotHeader/index.ts +1 -0
- package/src/ChatbotModal/ChatbotModal.scss +1 -1
- package/src/DeepThinking/DeepThinking.scss +24 -0
- package/src/DeepThinking/DeepThinking.test.tsx +61 -0
- package/src/DeepThinking/DeepThinking.tsx +68 -0
- package/src/DeepThinking/index.ts +3 -0
- package/src/FileDetails/FileDetails.scss +10 -0
- package/src/FileDetails/FileDetails.test.tsx +16 -0
- package/src/FileDetails/FileDetails.tsx +89 -32
- package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +20 -14
- package/src/FileDetailsLabel/FileDetailsLabel.test.tsx +21 -1
- package/src/FileDetailsLabel/FileDetailsLabel.tsx +16 -3
- package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +20 -14
- package/src/FilePreview/FilePreview.scss +22 -0
- package/src/FilePreview/FilePreview.test.tsx +112 -0
- package/src/FilePreview/FilePreview.tsx +58 -0
- package/src/FilePreview/index.ts +3 -0
- package/src/ImagePreview/ImagePreview.scss +61 -0
- package/src/ImagePreview/ImagePreview.test.tsx +253 -0
- package/src/ImagePreview/ImagePreview.tsx +200 -0
- package/src/ImagePreview/index.ts +3 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.scss +2 -1
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +6 -5
- package/src/Message/LinkMessage/LinkMessage.tsx +6 -2
- package/src/Message/ListMessage/ListItemMessage.tsx +5 -1
- package/src/Message/ListMessage/ListMessage.scss +17 -0
- package/src/Message/Message.scss +44 -0
- package/src/Message/Message.test.tsx +90 -0
- package/src/Message/Message.tsx +171 -46
- package/src/Message/SuperscriptMessage/SuperscriptMessage.scss +8 -0
- package/src/Message/SuperscriptMessage/SuperscriptMessage.tsx +13 -0
- package/src/Message/TextMessage/TextMessage.scss +46 -5
- package/src/Message/UserFeedback/UserFeedback.test.tsx +107 -0
- package/src/Message/UserFeedback/UserFeedback.tsx +41 -6
- package/src/MessageBar/MessageBar.tsx +23 -3
- package/src/MessageBox/JumpButton.test.tsx +4 -4
- package/src/MessageBox/JumpButton.tsx +20 -4
- package/src/MessageBox/MessageBox.scss +0 -12
- package/src/MessageBox/MessageBox.test.tsx +2 -2
- package/src/MessageBox/MessageBox.tsx +23 -2
- package/src/SourcesCard/SourcesCard.scss +17 -0
- package/src/SourcesCard/SourcesCard.test.tsx +93 -0
- package/src/SourcesCard/SourcesCard.tsx +116 -80
- package/src/ToolResponse/ToolResponse.scss +36 -0
- package/src/ToolResponse/ToolResponse.test.tsx +78 -0
- package/src/ToolResponse/ToolResponse.tsx +95 -0
- package/src/ToolResponse/index.ts +3 -0
- package/src/index.ts +12 -0
- package/src/main.scss +16 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("@testing-library/react");
|
|
8
|
+
require("@testing-library/jest-dom");
|
|
9
|
+
const ImagePreview_1 = __importDefault(require("./ImagePreview"));
|
|
10
|
+
const Chatbot_1 = require("../Chatbot");
|
|
11
|
+
const mockImages = [
|
|
12
|
+
{
|
|
13
|
+
fileName: 'image1.jpg',
|
|
14
|
+
fileSize: '2.5 MB',
|
|
15
|
+
image: (0, jsx_runtime_1.jsx)("img", { src: "", alt: "Test image 1" })
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
fileName: 'image2.png',
|
|
19
|
+
fileSize: '1.8 MB',
|
|
20
|
+
image: (0, jsx_runtime_1.jsx)("img", { src: "", alt: "Test image 2" })
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
fileName: 'image3.gif',
|
|
24
|
+
image: (0, jsx_runtime_1.jsx)("img", { src: "", alt: "Test image 3" })
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
const defaultProps = {
|
|
28
|
+
isModalOpen: true,
|
|
29
|
+
handleModalToggle: jest.fn(),
|
|
30
|
+
images: mockImages
|
|
31
|
+
};
|
|
32
|
+
describe('ImagePreview', () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
jest.clearAllMocks();
|
|
35
|
+
});
|
|
36
|
+
it('renders modal when isModalOpen is true', () => {
|
|
37
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps)));
|
|
38
|
+
expect(react_1.screen.getByRole('dialog')).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
it('does not render modal when isModalOpen is false', () => {
|
|
41
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { isModalOpen: false })));
|
|
42
|
+
expect(react_1.screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
it('displays custom title when provided', () => {
|
|
45
|
+
const customTitle = 'Custom image preview';
|
|
46
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { title: customTitle })));
|
|
47
|
+
expect(react_1.screen.getByRole('heading', { name: customTitle })).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
it('displays default title when no title provided', () => {
|
|
50
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps)));
|
|
51
|
+
expect(react_1.screen.getByRole('heading', { name: /Preview images/i })).toBeInTheDocument();
|
|
52
|
+
});
|
|
53
|
+
it('calls handleModalToggle when modal is closed', () => {
|
|
54
|
+
const mockHandleToggle = jest.fn();
|
|
55
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { handleModalToggle: mockHandleToggle })));
|
|
56
|
+
const closeButton = react_1.screen.getByRole('button', { name: /close/i });
|
|
57
|
+
react_1.fireEvent.click(closeButton);
|
|
58
|
+
expect(mockHandleToggle).toHaveBeenCalledTimes(1);
|
|
59
|
+
});
|
|
60
|
+
it('displays first image by default', () => {
|
|
61
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps)));
|
|
62
|
+
expect(react_1.screen.getByText('image1.jpg')).toBeInTheDocument();
|
|
63
|
+
expect(react_1.screen.getByText('2.5 MB')).toBeInTheDocument();
|
|
64
|
+
expect(react_1.screen.getByAltText('Test image 1')).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
it('displays page counter correctly', () => {
|
|
67
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps)));
|
|
68
|
+
expect(react_1.screen.getByText('1/3')).toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
it('navigates to next image when next button is clicked', () => {
|
|
71
|
+
const mockOnNextClick = jest.fn();
|
|
72
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { onNextClick: mockOnNextClick })));
|
|
73
|
+
const nextButton = react_1.screen.getByRole('button', { name: /Go to next image/i });
|
|
74
|
+
react_1.fireEvent.click(nextButton);
|
|
75
|
+
expect(mockOnNextClick).toHaveBeenCalled();
|
|
76
|
+
expect(react_1.screen.getByText('2/3')).toBeInTheDocument();
|
|
77
|
+
expect(react_1.screen.getByText('image2.png')).toBeInTheDocument();
|
|
78
|
+
});
|
|
79
|
+
it('navigates to previous image when previous button is clicked', () => {
|
|
80
|
+
const mockOnPreviousClick = jest.fn();
|
|
81
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { onPreviousClick: mockOnPreviousClick })));
|
|
82
|
+
// First go to page 2
|
|
83
|
+
const nextButton = react_1.screen.getByRole('button', { name: /Go to next image/i });
|
|
84
|
+
react_1.fireEvent.click(nextButton);
|
|
85
|
+
// Then go back to page 1
|
|
86
|
+
const previousButton = react_1.screen.getByRole('button', { name: /Go to previous image/i });
|
|
87
|
+
react_1.fireEvent.click(previousButton);
|
|
88
|
+
expect(mockOnPreviousClick).toHaveBeenCalled();
|
|
89
|
+
expect(react_1.screen.getByText('1/3')).toBeInTheDocument();
|
|
90
|
+
});
|
|
91
|
+
it('calls onSetPage when page changes', () => {
|
|
92
|
+
const mockOnSetPage = jest.fn();
|
|
93
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { onSetPage: mockOnSetPage })));
|
|
94
|
+
const nextButton = react_1.screen.getByRole('button', { name: /Go to next image/i });
|
|
95
|
+
react_1.fireEvent.click(nextButton);
|
|
96
|
+
expect(mockOnSetPage).toHaveBeenCalledWith(expect.any(Object), 2);
|
|
97
|
+
});
|
|
98
|
+
it('disables previous button on first page', () => {
|
|
99
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps)));
|
|
100
|
+
const previousButton = react_1.screen.getByRole('button', { name: /Go to previous image/i });
|
|
101
|
+
expect(previousButton).toBeDisabled();
|
|
102
|
+
});
|
|
103
|
+
it('disables next button on last page', () => {
|
|
104
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps)));
|
|
105
|
+
// Navigate to last page
|
|
106
|
+
const nextButton = react_1.screen.getByRole('button', { name: /Go to next image/i });
|
|
107
|
+
react_1.fireEvent.click(nextButton); // page 2
|
|
108
|
+
react_1.fireEvent.click(nextButton); // page 3
|
|
109
|
+
expect(nextButton).toBeDisabled();
|
|
110
|
+
});
|
|
111
|
+
it('disables both navigation buttons when isDisabled is true', () => {
|
|
112
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { isDisabled: true })));
|
|
113
|
+
const previousButton = react_1.screen.getByRole('button', { name: /Go to previous image/i });
|
|
114
|
+
const nextButton = react_1.screen.getByRole('button', { name: /Go to next image/i });
|
|
115
|
+
expect(previousButton).toBeDisabled();
|
|
116
|
+
expect(nextButton).toBeDisabled();
|
|
117
|
+
});
|
|
118
|
+
it('uses custom aria labels for pagination', () => {
|
|
119
|
+
const customLabels = {
|
|
120
|
+
paginationAriaLabel: 'Custom pagination',
|
|
121
|
+
toPreviousPageAriaLabel: 'Go to previous image',
|
|
122
|
+
toNextPageAriaLabel: 'Go to next image'
|
|
123
|
+
};
|
|
124
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, customLabels)));
|
|
125
|
+
expect(react_1.screen.getByRole('navigation', { name: 'Custom pagination' })).toBeInTheDocument();
|
|
126
|
+
expect(react_1.screen.getByRole('button', { name: 'Go to previous image' })).toBeInTheDocument();
|
|
127
|
+
expect(react_1.screen.getByRole('button', { name: 'Go to next image' })).toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
it('renders with compact mode when isCompact is true', () => {
|
|
130
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { isCompact: true })));
|
|
131
|
+
const modal = react_1.screen.getByRole('dialog');
|
|
132
|
+
expect(modal).toHaveClass('pf-m-compact');
|
|
133
|
+
});
|
|
134
|
+
it('applies custom className when provided', () => {
|
|
135
|
+
const customClassName = 'custom-image-preview';
|
|
136
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { className: customClassName })));
|
|
137
|
+
const modal = react_1.screen.getByRole('dialog');
|
|
138
|
+
expect(modal).toHaveClass(customClassName);
|
|
139
|
+
});
|
|
140
|
+
it('applies display mode class correctly', () => {
|
|
141
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { displayMode: Chatbot_1.ChatbotDisplayMode.embedded })));
|
|
142
|
+
const modal = react_1.screen.getByRole('dialog');
|
|
143
|
+
expect(modal).toHaveClass('pf-chatbot__image-preview-modal--embedded');
|
|
144
|
+
});
|
|
145
|
+
it('passes additional props to ChatbotModal', () => {
|
|
146
|
+
const modalClass = 'custom-modal-class';
|
|
147
|
+
const additionalProps = {
|
|
148
|
+
'data-testid': 'modal',
|
|
149
|
+
className: modalClass
|
|
150
|
+
};
|
|
151
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, additionalProps)));
|
|
152
|
+
const modal = react_1.screen.getByTestId('modal');
|
|
153
|
+
expect(modal).toBeInTheDocument();
|
|
154
|
+
expect(modal).toBeInTheDocument();
|
|
155
|
+
expect(modal).toHaveClass(modalClass);
|
|
156
|
+
});
|
|
157
|
+
it('passes modalHeaderProps correctly', () => {
|
|
158
|
+
const headerClass = 'custom-modal-header-class';
|
|
159
|
+
const headerProps = {
|
|
160
|
+
'data-testid': 'header',
|
|
161
|
+
className: headerClass
|
|
162
|
+
};
|
|
163
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { modalHeaderProps: headerProps })));
|
|
164
|
+
expect(react_1.screen.getByTestId('header')).toBeInTheDocument();
|
|
165
|
+
expect(react_1.screen.getByTestId('header')).toHaveClass(headerClass);
|
|
166
|
+
});
|
|
167
|
+
it('passes modalBodyProps correctly', () => {
|
|
168
|
+
const bodyClass = 'custom-modal-body-class';
|
|
169
|
+
const bodyProps = {
|
|
170
|
+
'data-testid': 'body',
|
|
171
|
+
className: bodyClass
|
|
172
|
+
};
|
|
173
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { modalBodyProps: bodyProps })));
|
|
174
|
+
expect(react_1.screen.getByTestId('body')).toBeInTheDocument();
|
|
175
|
+
expect(react_1.screen.getByTestId('body')).toHaveClass(bodyClass);
|
|
176
|
+
});
|
|
177
|
+
it('handles single image without pagination', () => {
|
|
178
|
+
const singleImage = [mockImages[0]];
|
|
179
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { images: singleImage })));
|
|
180
|
+
expect(react_1.screen.queryByText('1/1')).not.toBeInTheDocument();
|
|
181
|
+
expect(react_1.screen.queryByRole('button', { name: /Go to previous image/i })).not.toBeInTheDocument();
|
|
182
|
+
expect(react_1.screen.queryByRole('button', { name: /Go to next image/i })).not.toBeInTheDocument();
|
|
183
|
+
});
|
|
184
|
+
it('calls onCloseFileDetailsLabel when file details close button is clicked', () => {
|
|
185
|
+
const mockOnClose = jest.fn();
|
|
186
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { onCloseFileDetailsLabel: mockOnClose })));
|
|
187
|
+
const closeButton = react_1.screen.getByRole('button', { name: /Close image1.jpg/i });
|
|
188
|
+
react_1.fireEvent.click(closeButton);
|
|
189
|
+
expect(mockOnClose).toHaveBeenCalled();
|
|
190
|
+
});
|
|
191
|
+
it('passes fileDetailsLabelProps correctly to FileDetailsLabel', () => {
|
|
192
|
+
const customFileDetailsProps = {
|
|
193
|
+
'data-testid': 'custom-file-details'
|
|
194
|
+
};
|
|
195
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { fileDetailsLabelProps: customFileDetailsProps })));
|
|
196
|
+
expect(react_1.screen.getByTestId('custom-file-details')).toBeInTheDocument();
|
|
197
|
+
});
|
|
198
|
+
it('displays file details for current page when navigating', () => {
|
|
199
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps)));
|
|
200
|
+
// Initially shows first image details
|
|
201
|
+
expect(react_1.screen.getByText('image1.jpg')).toBeInTheDocument();
|
|
202
|
+
expect(react_1.screen.getByText('2.5 MB')).toBeInTheDocument();
|
|
203
|
+
// Navigate to second page
|
|
204
|
+
const nextButton = react_1.screen.getByRole('button', { name: /Go to next image/i });
|
|
205
|
+
react_1.fireEvent.click(nextButton);
|
|
206
|
+
// Should now show second image details
|
|
207
|
+
expect(react_1.screen.getByText('image2.png')).toBeInTheDocument();
|
|
208
|
+
expect(react_1.screen.getByText('1.8 MB')).toBeInTheDocument();
|
|
209
|
+
// Navigate to third page
|
|
210
|
+
react_1.fireEvent.click(nextButton);
|
|
211
|
+
// Should now show third image details (no file size)
|
|
212
|
+
expect(react_1.screen.getByText('image3.gif')).toBeInTheDocument();
|
|
213
|
+
expect(react_1.screen.queryByText(/MB/)).not.toBeInTheDocument();
|
|
214
|
+
});
|
|
215
|
+
it('sets hasTruncation to false on FileDetailsLabel', () => {
|
|
216
|
+
const longFileName = 'very-long-filename-that-would-normally-be-truncated-in-other-contexts.jpg';
|
|
217
|
+
const imageWithLongName = {
|
|
218
|
+
fileName: longFileName,
|
|
219
|
+
fileSize: '1.0 MB',
|
|
220
|
+
image: (0, jsx_runtime_1.jsx)("img", { src: "", alt: "Test image with long name" })
|
|
221
|
+
};
|
|
222
|
+
(0, react_1.render)((0, jsx_runtime_1.jsx)(ImagePreview_1.default, Object.assign({}, defaultProps, { images: [imageWithLongName] })));
|
|
223
|
+
expect(react_1.screen.getByText(longFileName)).toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -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 ImagePreview_1 = require("./ImagePreview");
|
|
22
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(ImagePreview_1).default; } });
|
|
23
|
+
__exportStar(require("./ImagePreview"), exports);
|
|
@@ -28,9 +28,9 @@ const CodeBlockMessage = (_a) => {
|
|
|
28
28
|
const [copied, setCopied] = (0, react_1.useState)(false);
|
|
29
29
|
const [isExpanded, setIsExpanded] = (0, react_1.useState)(false);
|
|
30
30
|
const buttonRef = (0, react_1.useRef)();
|
|
31
|
-
const tooltipID = (0,
|
|
32
|
-
const toggleId = (0,
|
|
33
|
-
const contentId = (0,
|
|
31
|
+
const tooltipID = (0, react_core_1.getUniqueId)();
|
|
32
|
+
const toggleId = (0, react_core_1.getUniqueId)();
|
|
33
|
+
const contentId = (0, react_core_1.getUniqueId)();
|
|
34
34
|
const codeBlockRef = (0, react_1.useRef)(null);
|
|
35
35
|
const language = (_b = /language-(\w+)/.exec(className || '')) === null || _b === void 0 ? void 0 : _b[1];
|
|
36
36
|
// Get custom toggle text from data attributes if available - for use with rehype plugins
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { ButtonProps } from '@patternfly/react-core';
|
|
2
|
-
|
|
2
|
+
import { ExtraProps } from 'react-markdown';
|
|
3
|
+
declare const LinkMessage: ({ children, target, href, id, ...props }: ButtonProps & ExtraProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
4
|
export default LinkMessage;
|
|
@@ -18,10 +18,14 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
18
18
|
const react_core_1 = require("@patternfly/react-core");
|
|
19
19
|
const react_icons_1 = require("@patternfly/react-icons");
|
|
20
20
|
const LinkMessage = (_a) => {
|
|
21
|
-
var { children, target, href } = _a, props = __rest(_a, ["children", "target", "href"]);
|
|
21
|
+
var { children, target, href, id } = _a, props = __rest(_a, ["children", "target", "href", "id"]);
|
|
22
22
|
if (target === '_blank') {
|
|
23
|
-
return ((0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ component: "a", variant: "link", href: href, icon: (0, jsx_runtime_1.jsx)(react_icons_1.ExternalLinkSquareAltIcon, {}), iconPosition: "end", isInline: true, target: target
|
|
23
|
+
return ((0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ component: "a", variant: "link", href: href, icon: (0, jsx_runtime_1.jsx)(react_icons_1.ExternalLinkSquareAltIcon, {}), iconPosition: "end", isInline: true, target: target,
|
|
24
|
+
// need to explicitly call this out or id doesn't seem to get passed - required for footnotes
|
|
25
|
+
id: id }, props, { children: children })));
|
|
24
26
|
}
|
|
25
|
-
return (
|
|
27
|
+
return (
|
|
28
|
+
// need to explicitly call this out or id doesn't seem to get passed - required for footnotes
|
|
29
|
+
(0, jsx_runtime_1.jsx)(react_core_1.Button, Object.assign({ isInline: true, component: "a", href: href, variant: "link", id: id }, props, { children: children })));
|
|
26
30
|
};
|
|
27
31
|
exports.default = LinkMessage;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ExtraProps } from 'react-markdown';
|
|
2
|
-
declare const ListItemMessage: ({ children }: JSX.IntrinsicElements["li"] & ExtraProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
declare const ListItemMessage: ({ children, ...props }: JSX.IntrinsicElements["li"] & ExtraProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export default ListItemMessage;
|
|
@@ -1,6 +1,21 @@
|
|
|
1
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
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
4
15
|
const react_core_1 = require("@patternfly/react-core");
|
|
5
|
-
const ListItemMessage = (
|
|
16
|
+
const ListItemMessage = (_a) => {
|
|
17
|
+
var _b;
|
|
18
|
+
var { children } = _a, props = __rest(_a, ["children"]);
|
|
19
|
+
return ((0, jsx_runtime_1.jsx)(react_core_1.ListItem, Object.assign({}, props, { tabIndex: ((_b = props === null || props === void 0 ? void 0 : props.id) === null || _b === void 0 ? void 0 : _b.includes('fn-')) ? -1 : props === null || props === void 0 ? void 0 : props.tabIndex, children: children })));
|
|
20
|
+
};
|
|
6
21
|
exports.default = ListItemMessage;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
|
|
3
|
+
import { Options } from 'react-markdown';
|
|
3
4
|
import { AlertProps, AvatarProps, ButtonProps, ExpandableSectionProps, ExpandableSectionToggleProps, FormProps, LabelGroupProps } from '@patternfly/react-core';
|
|
4
5
|
import { ActionProps } from '../ResponseActions/ResponseActions';
|
|
5
6
|
import { SourcesCardProps } from '../SourcesCard';
|
|
@@ -9,6 +10,8 @@ import { UserFeedbackProps } from './UserFeedback/UserFeedback';
|
|
|
9
10
|
import { UserFeedbackCompleteProps } from './UserFeedback/UserFeedbackComplete';
|
|
10
11
|
import { TableProps } from '@patternfly/react-table';
|
|
11
12
|
import { PluggableList } from 'unified';
|
|
13
|
+
import { ToolResponseProps } from '../ToolResponse';
|
|
14
|
+
import { DeepThinkingProps } from '../DeepThinking';
|
|
12
15
|
export interface MessageAttachment {
|
|
13
16
|
/** Name of file attached to the message */
|
|
14
17
|
name: string;
|
|
@@ -116,6 +119,8 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
116
119
|
tableProps?: Required<Pick<TableProps, 'aria-label'>> & TableProps;
|
|
117
120
|
/** Additional rehype plugins passed from the consumer */
|
|
118
121
|
additionalRehypePlugins?: PluggableList;
|
|
122
|
+
/** Additional remark plugins passed from the consumer */
|
|
123
|
+
additionalRemarkPlugins?: PluggableList;
|
|
119
124
|
/** Whether to open links in message in new tab. */
|
|
120
125
|
openLinkInNewTab?: boolean;
|
|
121
126
|
/** Optional inline error message that can be displayed in the message */
|
|
@@ -140,6 +145,16 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
140
145
|
editFormProps?: FormProps;
|
|
141
146
|
/** Sets message to compact styling. */
|
|
142
147
|
isCompact?: boolean;
|
|
148
|
+
/** Disables markdown parsing for message, allowing only text input */
|
|
149
|
+
isMarkdownDisabled?: boolean;
|
|
150
|
+
/** 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 */
|
|
151
|
+
reactMarkdownProps?: Options;
|
|
152
|
+
/** Props for tool response card */
|
|
153
|
+
toolResponse?: ToolResponseProps;
|
|
154
|
+
/** Props for deep thinking card */
|
|
155
|
+
deepThinking?: DeepThinkingProps;
|
|
156
|
+
/** Allows passing additional props down to remark-gfm. See https://github.com/remarkjs/remark-gfm?tab=readme-ov-file#options for options */
|
|
157
|
+
remarkGfmProps?: Options;
|
|
143
158
|
}
|
|
144
159
|
export declare const MessageBase: FunctionComponent<MessageProps>;
|
|
145
160
|
declare const Message: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -50,8 +50,11 @@ const LinkMessage_1 = __importDefault(require("./LinkMessage/LinkMessage"));
|
|
|
50
50
|
const ErrorMessage_1 = __importDefault(require("./ErrorMessage/ErrorMessage"));
|
|
51
51
|
const MessageInput_1 = __importDefault(require("./MessageInput"));
|
|
52
52
|
const rehypeMoveImagesOutOfParagraphs_1 = require("./Plugins/rehypeMoveImagesOutOfParagraphs");
|
|
53
|
+
const ToolResponse_1 = __importDefault(require("../ToolResponse"));
|
|
54
|
+
const DeepThinking_1 = __importDefault(require("../DeepThinking"));
|
|
55
|
+
const SuperscriptMessage_1 = __importDefault(require("./SuperscriptMessage/SuperscriptMessage"));
|
|
53
56
|
const MessageBase = (_a) => {
|
|
54
|
-
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, inputRef, editFormProps, isCompact } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "inputRef", "editFormProps", "isCompact"]);
|
|
57
|
+
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], additionalRemarkPlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, inputRef, editFormProps, isCompact, isMarkdownDisabled, reactMarkdownProps, toolResponse, deepThinking, remarkGfmProps } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "additionalRemarkPlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "inputRef", "editFormProps", "isCompact", "isMarkdownDisabled", "reactMarkdownProps", "toolResponse", "deepThinking", "remarkGfmProps"]);
|
|
55
58
|
const [messageText, setMessageText] = (0, react_1.useState)(content);
|
|
56
59
|
(0, react_1.useEffect)(() => {
|
|
57
60
|
setMessageText(content);
|
|
@@ -73,6 +76,129 @@ const MessageBase = (_a) => {
|
|
|
73
76
|
// Keep timestamps consistent between Timestamp component and aria-label
|
|
74
77
|
const date = new Date();
|
|
75
78
|
const dateString = timestamp !== null && timestamp !== void 0 ? timestamp : `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
79
|
+
const handleMarkdown = () => {
|
|
80
|
+
if (isMarkdownDisabled) {
|
|
81
|
+
return ((0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.p }, props, { children: messageText })));
|
|
82
|
+
}
|
|
83
|
+
return ((0, jsx_runtime_1.jsx)(react_markdown_1.default, Object.assign({ components: {
|
|
84
|
+
section: (props) => {
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
86
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
87
|
+
return (0, jsx_runtime_1.jsx)("section", Object.assign({}, rest, { className: `pf-chatbot__message-text ${rest === null || rest === void 0 ? void 0 : rest.className}` }));
|
|
88
|
+
},
|
|
89
|
+
p: (props) => {
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
91
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
92
|
+
return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.p }, rest));
|
|
93
|
+
},
|
|
94
|
+
code: (_a) => {
|
|
95
|
+
var { children } = _a, props = __rest(_a, ["children"]);
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
97
|
+
const { node } = props, codeProps = __rest(props, ["node"]);
|
|
98
|
+
return ((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, Object.assign({}, codeProps, codeBlockProps, { children: children })));
|
|
99
|
+
},
|
|
100
|
+
h1: (props) => {
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
102
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
103
|
+
return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h1 }, rest));
|
|
104
|
+
},
|
|
105
|
+
h2: (props) => {
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
107
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
108
|
+
return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h2 }, rest));
|
|
109
|
+
},
|
|
110
|
+
h3: (props) => {
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
112
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
113
|
+
return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h3 }, rest));
|
|
114
|
+
},
|
|
115
|
+
h4: (props) => {
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
117
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
118
|
+
return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h4 }, rest));
|
|
119
|
+
},
|
|
120
|
+
h5: (props) => {
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
122
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
123
|
+
return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h5 }, rest));
|
|
124
|
+
},
|
|
125
|
+
h6: (props) => {
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
127
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
128
|
+
return (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h6 }, rest));
|
|
129
|
+
},
|
|
130
|
+
blockquote: (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)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.blockquote }, rest));
|
|
134
|
+
},
|
|
135
|
+
ul: (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)(UnorderedListMessage_1.default, Object.assign({}, rest));
|
|
139
|
+
},
|
|
140
|
+
ol: (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)(OrderedListMessage_1.default, Object.assign({}, rest));
|
|
144
|
+
},
|
|
145
|
+
li: (props) => {
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
147
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
148
|
+
return (0, jsx_runtime_1.jsx)(ListItemMessage_1.default, Object.assign({}, rest));
|
|
149
|
+
},
|
|
150
|
+
// table requires node attribute for calculating headers for mobile breakpoint
|
|
151
|
+
table: (props) => (0, jsx_runtime_1.jsx)(TableMessage_1.default, Object.assign({}, props, tableProps)),
|
|
152
|
+
tbody: (props) => {
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
154
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
155
|
+
return (0, jsx_runtime_1.jsx)(TbodyMessage_1.default, Object.assign({}, rest));
|
|
156
|
+
},
|
|
157
|
+
thead: (props) => {
|
|
158
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
159
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
160
|
+
return (0, jsx_runtime_1.jsx)(TheadMessage_1.default, Object.assign({}, rest));
|
|
161
|
+
},
|
|
162
|
+
tr: (props) => {
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
164
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
165
|
+
return (0, jsx_runtime_1.jsx)(TrMessage_1.default, Object.assign({}, rest));
|
|
166
|
+
},
|
|
167
|
+
td: (props) => {
|
|
168
|
+
// Conflicts with Td type
|
|
169
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
170
|
+
const { node, width } = props, rest = __rest(props, ["node", "width"]);
|
|
171
|
+
return (0, jsx_runtime_1.jsx)(TdMessage_1.default, Object.assign({}, rest));
|
|
172
|
+
},
|
|
173
|
+
th: (props) => {
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
175
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
176
|
+
return (0, jsx_runtime_1.jsx)(ThMessage_1.default, Object.assign({}, rest));
|
|
177
|
+
},
|
|
178
|
+
img: (props) => {
|
|
179
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
180
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
181
|
+
return (0, jsx_runtime_1.jsx)(ImageMessage_1.default, Object.assign({}, rest));
|
|
182
|
+
},
|
|
183
|
+
a: (props) => {
|
|
184
|
+
// node is just the details of the document structure - not needed
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
186
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
187
|
+
return (
|
|
188
|
+
// some a types conflict with ButtonProps, but it's ok because we are using an a tag
|
|
189
|
+
// there are too many to handle manually
|
|
190
|
+
(0, jsx_runtime_1.jsx)(LinkMessage_1.default, Object.assign({}, rest, linkProps, { children: props.children })));
|
|
191
|
+
},
|
|
192
|
+
// used for footnotes
|
|
193
|
+
sup: (props) => {
|
|
194
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
195
|
+
const { node } = props, rest = __rest(props, ["node"]);
|
|
196
|
+
return (0, jsx_runtime_1.jsx)(SuperscriptMessage_1.default, Object.assign({}, rest));
|
|
197
|
+
}
|
|
198
|
+
}, remarkPlugins: [[remark_gfm_1.default, Object.assign({}, remarkGfmProps)], ...additionalRemarkPlugins], rehypePlugins: rehypePlugins }, reactMarkdownProps, { remarkRehypeOptions: Object.assign({
|
|
199
|
+
// removes sr-only class from footnote labels applied by default
|
|
200
|
+
footnoteLabelProperties: { className: [''] } }, reactMarkdownProps === null || reactMarkdownProps === void 0 ? void 0 : reactMarkdownProps.remarkRehypeOptions), children: messageText })));
|
|
201
|
+
};
|
|
76
202
|
const renderMessage = () => {
|
|
77
203
|
if (isLoading) {
|
|
78
204
|
return (0, jsx_runtime_1.jsx)(MessageLoading_1.default, { loadingWord: loadingWord });
|
|
@@ -83,38 +209,9 @@ const MessageBase = (_a) => {
|
|
|
83
209
|
setMessageText(value);
|
|
84
210
|
}, onEditCancel: onEditCancel, inputRef: inputRef }, editFormProps))] }));
|
|
85
211
|
}
|
|
86
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [beforeMainContent && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: beforeMainContent }), error ? (
|
|
87
|
-
p: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.p }, props)),
|
|
88
|
-
code: (_a) => {
|
|
89
|
-
var { children } = _a, props = __rest(_a, ["children"]);
|
|
90
|
-
return ((0, jsx_runtime_1.jsx)(CodeBlockMessage_1.default, Object.assign({}, props, codeBlockProps, { children: children })));
|
|
91
|
-
},
|
|
92
|
-
h1: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h1 }, props)),
|
|
93
|
-
h2: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h2 }, props)),
|
|
94
|
-
h3: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h3 }, props)),
|
|
95
|
-
h4: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h4 }, props)),
|
|
96
|
-
h5: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h5 }, props)),
|
|
97
|
-
h6: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.h6 }, props)),
|
|
98
|
-
blockquote: (props) => (0, jsx_runtime_1.jsx)(TextMessage_1.default, Object.assign({ component: react_core_1.ContentVariants.blockquote }, props)),
|
|
99
|
-
ul: (props) => (0, jsx_runtime_1.jsx)(UnorderedListMessage_1.default, Object.assign({}, props)),
|
|
100
|
-
ol: (props) => (0, jsx_runtime_1.jsx)(OrderedListMessage_1.default, Object.assign({}, props)),
|
|
101
|
-
li: (props) => (0, jsx_runtime_1.jsx)(ListItemMessage_1.default, Object.assign({}, props)),
|
|
102
|
-
table: (props) => (0, jsx_runtime_1.jsx)(TableMessage_1.default, Object.assign({}, props, tableProps)),
|
|
103
|
-
tbody: (props) => (0, jsx_runtime_1.jsx)(TbodyMessage_1.default, Object.assign({}, props)),
|
|
104
|
-
thead: (props) => (0, jsx_runtime_1.jsx)(TheadMessage_1.default, Object.assign({}, props)),
|
|
105
|
-
tr: (props) => (0, jsx_runtime_1.jsx)(TrMessage_1.default, Object.assign({}, props)),
|
|
106
|
-
td: (props) => {
|
|
107
|
-
// Conflicts with Td type
|
|
108
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
109
|
-
const { width } = props, rest = __rest(props, ["width"]);
|
|
110
|
-
return (0, jsx_runtime_1.jsx)(TdMessage_1.default, Object.assign({}, rest));
|
|
111
|
-
},
|
|
112
|
-
th: (props) => (0, jsx_runtime_1.jsx)(ThMessage_1.default, Object.assign({}, props)),
|
|
113
|
-
img: (props) => (0, jsx_runtime_1.jsx)(ImageMessage_1.default, Object.assign({}, props)),
|
|
114
|
-
a: (props) => ((0, jsx_runtime_1.jsx)(LinkMessage_1.default, Object.assign({ href: props.href, rel: props.rel, target: props.target }, linkProps, { children: props.children })))
|
|
115
|
-
}, remarkPlugins: [remark_gfm_1.default], rehypePlugins: rehypePlugins, children: messageText }))] }));
|
|
212
|
+
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [beforeMainContent && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: beforeMainContent }), error ? (0, jsx_runtime_1.jsx)(ErrorMessage_1.default, Object.assign({}, error)) : handleMarkdown()] }));
|
|
116
213
|
};
|
|
117
|
-
return ((0, jsx_runtime_1.jsxs)("section", Object.assign({ "aria-label": `Message from ${role} - ${dateString}`, className: `pf-chatbot__message pf-chatbot__message--${role}`, "aria-live": isLiveRegion ? 'polite' : undefined, "aria-atomic": isLiveRegion ? false : undefined, ref: innerRef }, props, { children: [(0, jsx_runtime_1.jsx)(react_core_1.Avatar, Object.assign({ className: `pf-chatbot__message-avatar ${hasRoundAvatar ? 'pf-chatbot__message-avatar--round' : ''} ${avatarClassName ? avatarClassName : ''}`, src: avatar, alt: "" }, avatarProps)), (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-contents", children: [(0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-meta", children: [name && ((0, jsx_runtime_1.jsx)("span", { className: "pf-chatbot__message-name", children: (0, jsx_runtime_1.jsx)(react_core_1.Truncate, { content: name }) })), role === 'bot' && ((0, jsx_runtime_1.jsx)(react_core_1.Label, { variant: "outline", isCompact: true, children: botWord })), (0, jsx_runtime_1.jsx)(react_core_1.Timestamp, { date: date, children: timestamp })] }), (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-response", children: [(0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-and-actions", children: [renderMessage(), afterMainContent && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: afterMainContent }), !isLoading && sources && (0, jsx_runtime_1.jsx)(SourcesCard_1.default, Object.assign({}, sources, { isCompact: isCompact })), quickStarts && quickStarts.quickStart && ((0, jsx_runtime_1.jsx)(QuickStartTile_1.default, { quickStart: quickStarts.quickStart, onSelectQuickStart: quickStarts.onSelectQuickStart, minuteWord: quickStarts.minuteWord, minuteWordPlural: quickStarts.minuteWordPlural, prerequisiteWord: quickStarts.prerequisiteWord, prerequisiteWordPlural: quickStarts.prerequisiteWordPlural, quickStartButtonAriaLabel: quickStarts.quickStartButtonAriaLabel, isCompact: isCompact })), !isLoading && !isEditable && actions && (0, jsx_runtime_1.jsx)(ResponseActions_1.default, { actions: actions }), userFeedbackForm && (0, jsx_runtime_1.jsx)(UserFeedback_1.default, Object.assign({}, userFeedbackForm, { timestamp: dateString, isCompact: isCompact })), userFeedbackComplete && ((0, jsx_runtime_1.jsx)(UserFeedbackComplete_1.default, Object.assign({}, userFeedbackComplete, { timestamp: dateString, isCompact: isCompact }))), !isLoading && quickResponses && ((0, jsx_runtime_1.jsx)(QuickResponse_1.default, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, isCompact: isCompact }))] }), attachments && ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__message-attachments-container", children: attachments.map((attachment) => {
|
|
214
|
+
return ((0, jsx_runtime_1.jsxs)("section", Object.assign({ "aria-label": `Message from ${role} - ${dateString}`, className: `pf-chatbot__message pf-chatbot__message--${role}`, "aria-live": isLiveRegion ? 'polite' : undefined, "aria-atomic": isLiveRegion ? false : undefined, ref: innerRef }, props, { children: [(0, jsx_runtime_1.jsx)(react_core_1.Avatar, Object.assign({ className: `pf-chatbot__message-avatar ${hasRoundAvatar ? 'pf-chatbot__message-avatar--round' : ''} ${avatarClassName ? avatarClassName : ''}`, src: avatar, alt: "" }, avatarProps)), (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-contents", children: [(0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-meta", children: [name && ((0, jsx_runtime_1.jsx)("span", { className: "pf-chatbot__message-name", children: (0, jsx_runtime_1.jsx)(react_core_1.Truncate, { content: name }) })), role === 'bot' && ((0, jsx_runtime_1.jsx)(react_core_1.Label, { variant: "outline", isCompact: true, children: botWord })), (0, jsx_runtime_1.jsx)(react_core_1.Timestamp, { date: date, children: timestamp })] }), (0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-response", children: [(0, jsx_runtime_1.jsxs)("div", { className: "pf-chatbot__message-and-actions", children: [renderMessage(), afterMainContent && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: afterMainContent }), toolResponse && (0, jsx_runtime_1.jsx)(ToolResponse_1.default, Object.assign({}, toolResponse)), deepThinking && (0, jsx_runtime_1.jsx)(DeepThinking_1.default, Object.assign({}, deepThinking)), !isLoading && sources && (0, jsx_runtime_1.jsx)(SourcesCard_1.default, Object.assign({}, sources, { isCompact: isCompact })), quickStarts && quickStarts.quickStart && ((0, jsx_runtime_1.jsx)(QuickStartTile_1.default, { quickStart: quickStarts.quickStart, onSelectQuickStart: quickStarts.onSelectQuickStart, minuteWord: quickStarts.minuteWord, minuteWordPlural: quickStarts.minuteWordPlural, prerequisiteWord: quickStarts.prerequisiteWord, prerequisiteWordPlural: quickStarts.prerequisiteWordPlural, quickStartButtonAriaLabel: quickStarts.quickStartButtonAriaLabel, isCompact: isCompact })), !isLoading && !isEditable && actions && (0, jsx_runtime_1.jsx)(ResponseActions_1.default, { actions: actions }), userFeedbackForm && (0, jsx_runtime_1.jsx)(UserFeedback_1.default, Object.assign({}, userFeedbackForm, { timestamp: dateString, isCompact: isCompact })), userFeedbackComplete && ((0, jsx_runtime_1.jsx)(UserFeedbackComplete_1.default, Object.assign({}, userFeedbackComplete, { timestamp: dateString, isCompact: isCompact }))), !isLoading && quickResponses && ((0, jsx_runtime_1.jsx)(QuickResponse_1.default, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, isCompact: isCompact }))] }), attachments && ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__message-attachments-container", children: attachments.map((attachment) => {
|
|
118
215
|
var _a;
|
|
119
216
|
return ((0, jsx_runtime_1.jsx)("div", { className: "pf-chatbot__message-attachment", children: (0, jsx_runtime_1.jsx)(FileDetailsLabel_1.default, { fileName: attachment.name, fileId: attachment.id, onClose: attachment.onClose, onClick: attachment.onClick, isLoading: attachment.isLoading, closeButtonAriaLabel: attachment.closeButtonAriaLabel, languageTestId: attachment.languageTestId, spinnerTestId: attachment.spinnerTestId }) }, (_a = attachment.id) !== null && _a !== void 0 ? _a : attachment.name));
|
|
120
217
|
}) })), !isLoading && endContent && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: endContent })] })] })] })));
|