@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
|
@@ -10,6 +10,7 @@ describe('FileDetails', () => {
|
|
|
10
10
|
it('should render file details correctly if an extension we support is passed in', () => {
|
|
11
11
|
render(_jsx(FileDetails, { fileName: "test.txt", languageTestId: "language" }));
|
|
12
12
|
expect(screen.getByText('test')).toBeTruthy();
|
|
13
|
+
expect(screen.queryByText('test.txt')).toBeFalsy();
|
|
13
14
|
expect(screen.getByText('TEXT')).toBeTruthy();
|
|
14
15
|
expect(screen.getByTestId('language')).toBeTruthy();
|
|
15
16
|
});
|
|
@@ -18,4 +19,19 @@ describe('FileDetails', () => {
|
|
|
18
19
|
expect(screen.getByText('test')).toBeTruthy();
|
|
19
20
|
expect(screen.queryByTestId('language')).toBeFalsy();
|
|
20
21
|
});
|
|
22
|
+
it('should support image formats by rendering extension differently', () => {
|
|
23
|
+
render(_jsx(FileDetails, { fileName: "test.svg", languageTestId: "language" }));
|
|
24
|
+
expect(screen.getByText('test')).toBeTruthy();
|
|
25
|
+
expect(screen.queryByText('test.svg')).toBeFalsy();
|
|
26
|
+
expect(screen.queryByTestId('language')).toBeFalsy();
|
|
27
|
+
});
|
|
28
|
+
it('should handle truncation differently', () => {
|
|
29
|
+
render(_jsx(FileDetails, { fileName: "test.svg", languageTestId: "language", hasTruncation: false }));
|
|
30
|
+
expect(screen.getByText('test.svg')).toBeTruthy();
|
|
31
|
+
expect(screen.queryByTestId('language')).toBeFalsy();
|
|
32
|
+
});
|
|
33
|
+
it('should include file size if prop passed in', () => {
|
|
34
|
+
render(_jsx(FileDetails, { fileName: "test.joke", languageTestId: "language", fileSize: "100MB" }));
|
|
35
|
+
expect(screen.getByText('100MB')).toBeTruthy();
|
|
36
|
+
});
|
|
21
37
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
|
-
interface FileDetailsLabelProps {
|
|
2
|
+
export interface FileDetailsLabelProps {
|
|
3
3
|
/** Name of file, including extension */
|
|
4
4
|
fileName: string;
|
|
5
5
|
/** Unique id of file */
|
|
@@ -16,6 +16,12 @@ interface FileDetailsLabelProps {
|
|
|
16
16
|
languageTestId?: string;
|
|
17
17
|
/** Custom test id for the loading spinner in the component */
|
|
18
18
|
spinnerTestId?: string;
|
|
19
|
+
/** File size */
|
|
20
|
+
fileSize?: string;
|
|
21
|
+
/** Whether to truncate file name */
|
|
22
|
+
hasTruncation?: boolean;
|
|
23
|
+
/** Icon used for close button */
|
|
24
|
+
closeButtonIcon?: React.ReactNode;
|
|
19
25
|
}
|
|
20
|
-
export declare const FileDetailsLabel: ({ fileName, fileId, isLoading, onClick, onClose, closeButtonAriaLabel, languageTestId, spinnerTestId }: PropsWithChildren<FileDetailsLabelProps>) => import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare const FileDetailsLabel: ({ fileName, fileId, isLoading, onClick, onClose, closeButtonAriaLabel, languageTestId, spinnerTestId, fileSize, hasTruncation, closeButtonIcon, ...props }: PropsWithChildren<FileDetailsLabelProps>) => import("react/jsx-runtime").JSX.Element;
|
|
21
27
|
export default FileDetailsLabel;
|
|
@@ -1,12 +1,24 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { Button, Label } from '@patternfly/react-core';
|
|
3
14
|
import FileDetails from '../FileDetails';
|
|
4
15
|
import { Spinner } from '@patternfly/react-core';
|
|
5
16
|
import { TimesIcon } from '@patternfly/react-icons';
|
|
6
|
-
export const FileDetailsLabel = (
|
|
17
|
+
export const FileDetailsLabel = (_a) => {
|
|
18
|
+
var { fileName, fileId, isLoading, onClick, onClose, closeButtonAriaLabel, languageTestId, spinnerTestId, fileSize, hasTruncation = true, closeButtonIcon = _jsx(TimesIcon, {}) } = _a, props = __rest(_a, ["fileName", "fileId", "isLoading", "onClick", "onClose", "closeButtonAriaLabel", "languageTestId", "spinnerTestId", "fileSize", "hasTruncation", "closeButtonIcon"]);
|
|
7
19
|
const handleClose = (event) => {
|
|
8
20
|
onClose && onClose(event, fileName, fileId);
|
|
9
21
|
};
|
|
10
|
-
return (_jsx(Label, Object.assign({ className: "pf-chatbot__file-label" }, (onClose && { onClose: (event) => onClose(event, fileName, fileId) }), { closeBtn: _jsx(Button, { type: "button", variant: "plain", "aria-label": closeButtonAriaLabel !== null && closeButtonAriaLabel !== void 0 ? closeButtonAriaLabel : `Close ${fileName}`, icon:
|
|
22
|
+
return (_jsx(Label, Object.assign({ className: "pf-chatbot__file-label" }, (onClose && { onClose: (event) => onClose(event, fileName, fileId) }), { closeBtn: _jsx(Button, { type: "button", variant: "plain", "aria-label": closeButtonAriaLabel !== null && closeButtonAriaLabel !== void 0 ? closeButtonAriaLabel : `Close ${fileName}`, icon: closeButtonIcon, onClick: handleClose }) }, (onClick && { onClick: (event) => onClick(event, fileName, fileId) }), props, { children: _jsxs("div", { className: "pf-chatbot__file-label-contents", children: [_jsx(FileDetails, { className: isLoading ? 'pf-chatbot__file-label-loading' : undefined, fileName: fileName, languageTestId: languageTestId, fileSize: fileSize, hasTruncation: hasTruncation }), isLoading && _jsx(Spinner, { "data-testid": spinnerTestId, size: "sm" })] }) })));
|
|
11
23
|
};
|
|
12
24
|
export default FileDetailsLabel;
|
|
@@ -12,6 +12,7 @@ import { render, screen } from '@testing-library/react';
|
|
|
12
12
|
import '@testing-library/jest-dom';
|
|
13
13
|
import FileDetailsLabel from './FileDetailsLabel';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
|
+
import { BellIcon } from '@patternfly/react-icons';
|
|
15
16
|
describe('FileDetailsLabel', () => {
|
|
16
17
|
it('should render file details label', () => {
|
|
17
18
|
const { container } = render(_jsx(FileDetailsLabel, { fileName: "test.txt" }));
|
|
@@ -27,6 +28,19 @@ describe('FileDetailsLabel', () => {
|
|
|
27
28
|
expect(screen.getByText('test')).toBeTruthy();
|
|
28
29
|
expect(screen.queryByTestId('language')).toBeFalsy();
|
|
29
30
|
});
|
|
31
|
+
it('should pass file size down', () => {
|
|
32
|
+
render(_jsx(FileDetailsLabel, { fileName: "test.svg", fileSize: "100MB" }));
|
|
33
|
+
expect(screen.getByText('100MB')).toBeTruthy();
|
|
34
|
+
});
|
|
35
|
+
it('should pass truncation prop down as true by default', () => {
|
|
36
|
+
render(_jsx(FileDetailsLabel, { fileName: "test.svg" }));
|
|
37
|
+
expect(screen.getByText('test')).toBeTruthy();
|
|
38
|
+
expect(screen.queryByText('test.svg')).toBeFalsy();
|
|
39
|
+
});
|
|
40
|
+
it('should pass truncation prop down when false', () => {
|
|
41
|
+
render(_jsx(FileDetailsLabel, { fileName: "test.svg", hasTruncation: false }));
|
|
42
|
+
expect(screen.getByText('test.svg')).toBeTruthy();
|
|
43
|
+
});
|
|
30
44
|
it('should not show spinner by default', () => {
|
|
31
45
|
render(_jsx(FileDetailsLabel, { fileName: "test.txt", spinnerTestId: "spinner" }));
|
|
32
46
|
expect(screen.queryByTestId('spinner')).toBeFalsy();
|
|
@@ -51,6 +65,10 @@ describe('FileDetailsLabel', () => {
|
|
|
51
65
|
}));
|
|
52
66
|
it('should use closeButtonAriaLabel prop appropriately', () => {
|
|
53
67
|
render(_jsx(FileDetailsLabel, { fileName: "test.txt", onClose: jest.fn(), closeButtonAriaLabel: "Delete file" }));
|
|
54
|
-
screen.getByRole('button', { name: /Delete file/i });
|
|
68
|
+
expect(screen.getByRole('button', { name: /Delete file/i })).toBeTruthy();
|
|
69
|
+
});
|
|
70
|
+
it('should support custom close icon', () => {
|
|
71
|
+
render(_jsx(FileDetailsLabel, { fileName: "test.txt", onClose: jest.fn(), closeButtonIcon: _jsx(BellIcon, { "data-testid": "bell" }) }));
|
|
72
|
+
expect(screen.getByTestId('bell')).toBeTruthy();
|
|
55
73
|
});
|
|
56
74
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ModalBodyProps, ModalHeaderProps } from '@patternfly/react-core';
|
|
2
|
+
import type { FunctionComponent } from 'react';
|
|
3
|
+
import { ChatbotDisplayMode } from '../Chatbot';
|
|
4
|
+
import { ChatbotModalProps } from '../ChatbotModal';
|
|
5
|
+
export interface FilePreviewProps extends ChatbotModalProps {
|
|
6
|
+
/** Class applied to modal */
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Function that handles modal toggle */
|
|
9
|
+
handleModalToggle: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
|
|
10
|
+
/** Whether modal is open */
|
|
11
|
+
isModalOpen: boolean;
|
|
12
|
+
/** Title of modal */
|
|
13
|
+
title?: string;
|
|
14
|
+
/** Display mode for the Chatbot parent; this influences the styles applied */
|
|
15
|
+
displayMode?: ChatbotDisplayMode;
|
|
16
|
+
/** File name */
|
|
17
|
+
fileName: string;
|
|
18
|
+
/** Sets modal to compact styling. */
|
|
19
|
+
isCompact?: boolean;
|
|
20
|
+
/** Additional props passed to modal header */
|
|
21
|
+
modalHeaderProps?: ModalHeaderProps;
|
|
22
|
+
/** Additional props passed to modal body */
|
|
23
|
+
modalBodyProps?: ModalBodyProps;
|
|
24
|
+
}
|
|
25
|
+
declare const FilePreview: FunctionComponent<FilePreviewProps>;
|
|
26
|
+
export default FilePreview;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { ModalBody, ModalHeader } from '@patternfly/react-core';
|
|
14
|
+
import { ChatbotDisplayMode } from '../Chatbot';
|
|
15
|
+
import ChatbotModal from '../ChatbotModal';
|
|
16
|
+
import { FileIcon } from '@patternfly/react-icons';
|
|
17
|
+
const FilePreview = (_a) => {
|
|
18
|
+
var { isModalOpen, displayMode = ChatbotDisplayMode.default, children, fileName, isCompact, className, handleModalToggle, title = 'File preview', modalHeaderProps, modalBodyProps } = _a, props = __rest(_a, ["isModalOpen", "displayMode", "children", "fileName", "isCompact", "className", "handleModalToggle", "title", "modalHeaderProps", "modalBodyProps"]);
|
|
19
|
+
return (_jsxs(ChatbotModal, Object.assign({ isOpen: isModalOpen, className: `pf-chatbot__file-preview-modal pf-chatbot__file-preview-modal--${displayMode} ${isCompact ? 'pf-m-compact' : ''} ${className ? className : ''}`, displayMode: displayMode, onClose: handleModalToggle, isCompact: isCompact }, props, { children: [_jsx(ModalHeader, Object.assign({ title: title }, modalHeaderProps)), _jsxs(ModalBody, Object.assign({ className: "pf-chatbot__file-preview-body" }, modalBodyProps, { children: [_jsx(FileIcon, { className: "pf-chatbot__file-preview-icon" }), _jsx("h2", { className: "pf-chatbot__file-preview-name", children: fileName }), children && _jsx("div", { className: "pf-chatbot__file-preview-body", children: children })] }))] })));
|
|
20
|
+
};
|
|
21
|
+
export default FilePreview;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import FilePreview from './FilePreview';
|
|
5
|
+
import { ChatbotDisplayMode } from '../Chatbot';
|
|
6
|
+
import { Button } from '@patternfly/react-core';
|
|
7
|
+
describe('FilePreview', () => {
|
|
8
|
+
const defaultProps = {
|
|
9
|
+
isModalOpen: true,
|
|
10
|
+
handleModalToggle: jest.fn(),
|
|
11
|
+
fileName: 'test-file.txt',
|
|
12
|
+
children: 'File content preview'
|
|
13
|
+
};
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
it('should render with basic props', () => {
|
|
18
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps)));
|
|
19
|
+
expect(screen.getByText('File preview')).toBeInTheDocument();
|
|
20
|
+
expect(screen.getByText('test-file.txt')).toBeInTheDocument();
|
|
21
|
+
});
|
|
22
|
+
it('should render with custom title', () => {
|
|
23
|
+
const customTitle = 'Custom file preview title';
|
|
24
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { title: customTitle })));
|
|
25
|
+
expect(screen.getByRole('heading', { name: customTitle })).toBeTruthy();
|
|
26
|
+
});
|
|
27
|
+
it('should handle modal toggle when closed', () => {
|
|
28
|
+
const mockToggle = jest.fn();
|
|
29
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { isModalOpen: false, handleModalToggle: mockToggle })));
|
|
30
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
it('should apply default display mode class', () => {
|
|
33
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps)));
|
|
34
|
+
const modal = screen.getByRole('dialog');
|
|
35
|
+
expect(modal).toHaveClass('pf-chatbot__file-preview-modal--default');
|
|
36
|
+
});
|
|
37
|
+
it('should apply custom display mode class', () => {
|
|
38
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { displayMode: ChatbotDisplayMode.fullscreen })));
|
|
39
|
+
const modal = screen.getByRole('dialog');
|
|
40
|
+
expect(modal).toHaveClass('pf-chatbot__file-preview-modal--fullscreen');
|
|
41
|
+
});
|
|
42
|
+
it('should apply compact styling when isCompact is true', () => {
|
|
43
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { isCompact: true })));
|
|
44
|
+
const modal = screen.getByRole('dialog');
|
|
45
|
+
expect(modal).toHaveClass('pf-m-compact');
|
|
46
|
+
});
|
|
47
|
+
it('should not apply compact styling when isCompact is false', () => {
|
|
48
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { isCompact: false })));
|
|
49
|
+
const modal = screen.getByRole('dialog');
|
|
50
|
+
expect(modal).not.toHaveClass('pf-m-compact');
|
|
51
|
+
});
|
|
52
|
+
it('should apply custom className', () => {
|
|
53
|
+
const customClass = 'custom-file-preview';
|
|
54
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { className: customClass })));
|
|
55
|
+
const modal = screen.getByRole('dialog');
|
|
56
|
+
expect(modal).toHaveClass(customClass);
|
|
57
|
+
});
|
|
58
|
+
it('should pass through additional props to ChatbotModal', () => {
|
|
59
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { "data-testid": "file-preview-modal" })));
|
|
60
|
+
const modal = screen.getByTestId('file-preview-modal');
|
|
61
|
+
expect(modal).toBeInTheDocument();
|
|
62
|
+
});
|
|
63
|
+
it('should pass modalHeaderProps to ModalHeader', () => {
|
|
64
|
+
const modalHeaderProps = {
|
|
65
|
+
'data-testid': 'custom-header'
|
|
66
|
+
};
|
|
67
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { modalHeaderProps: modalHeaderProps })));
|
|
68
|
+
const header = screen.getByTestId('custom-header');
|
|
69
|
+
expect(header).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
it('should pass modalBodyProps to ModalBody', () => {
|
|
72
|
+
const modalBodyProps = {
|
|
73
|
+
'data-testid': 'custom-body'
|
|
74
|
+
};
|
|
75
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { modalBodyProps: modalBodyProps })));
|
|
76
|
+
const body = screen.getByTestId('custom-body');
|
|
77
|
+
expect(body).toBeInTheDocument();
|
|
78
|
+
});
|
|
79
|
+
it('should pass ouiaId to ChatbotModal', () => {
|
|
80
|
+
const ouiaId = 'file-preview-ouia-id';
|
|
81
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { ouiaId: ouiaId })));
|
|
82
|
+
const modal = screen.getByRole('dialog');
|
|
83
|
+
expect(modal).toHaveAttribute('data-ouia-component-id', ouiaId);
|
|
84
|
+
});
|
|
85
|
+
it('should handle complex children', () => {
|
|
86
|
+
const complexChildren = (_jsxs("div", { children: [_jsx("h3", { children: "File details" }), _jsx("p", { children: "Size: 1.2 MB" }), _jsx(Button, { children: "Download" })] }));
|
|
87
|
+
render(_jsx(FilePreview, Object.assign({}, defaultProps, { children: complexChildren })));
|
|
88
|
+
expect(screen.getByRole('heading', { name: /File details/i })).toBeTruthy();
|
|
89
|
+
expect(screen.getByText('Size: 1.2 MB')).toBeTruthy();
|
|
90
|
+
expect(screen.getByRole('button', { name: /Download/i })).toBeTruthy();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ModalBodyProps, ModalHeaderProps } from '@patternfly/react-core';
|
|
2
|
+
import { type FunctionComponent } from 'react';
|
|
3
|
+
import { ChatbotDisplayMode } from '../Chatbot';
|
|
4
|
+
import { ChatbotModalProps } from '../ChatbotModal';
|
|
5
|
+
import { FileDetailsLabelProps } from '../FileDetailsLabel';
|
|
6
|
+
export interface ImagePreviewProps extends Omit<ChatbotModalProps, 'children'> {
|
|
7
|
+
/** Class applied to modal */
|
|
8
|
+
className?: string;
|
|
9
|
+
/** Function that handles modal toggle */
|
|
10
|
+
handleModalToggle: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
|
|
11
|
+
/** Whether modal is open */
|
|
12
|
+
isModalOpen: boolean;
|
|
13
|
+
/** Title of modal */
|
|
14
|
+
title?: string;
|
|
15
|
+
/** Display mode for the Chatbot parent; this influences the styles applied */
|
|
16
|
+
displayMode?: ChatbotDisplayMode;
|
|
17
|
+
/** Sets modal to compact styling. */
|
|
18
|
+
isCompact?: boolean;
|
|
19
|
+
/** Additional props passed to modal header */
|
|
20
|
+
modalHeaderProps?: ModalHeaderProps;
|
|
21
|
+
/** Additional props passed to modal body */
|
|
22
|
+
modalBodyProps?: ModalBodyProps;
|
|
23
|
+
/** Images displayed in modal */
|
|
24
|
+
images: {
|
|
25
|
+
fileName: string;
|
|
26
|
+
fileSize?: string;
|
|
27
|
+
image: React.ReactNode;
|
|
28
|
+
}[];
|
|
29
|
+
/** Flag indicating if the pagination is disabled. */
|
|
30
|
+
isDisabled?: boolean;
|
|
31
|
+
/** Accessible label for the pagination component. */
|
|
32
|
+
paginationAriaLabel?: string;
|
|
33
|
+
/** Accessible label for the button which moves to the next page. */
|
|
34
|
+
toNextPageAriaLabel?: string;
|
|
35
|
+
/** Accessible label for the button which moves to the previous page. */
|
|
36
|
+
toPreviousPageAriaLabel?: string;
|
|
37
|
+
/** Function called when user clicks to navigate to next page. */
|
|
38
|
+
onNextClick?: (event: React.SyntheticEvent<HTMLButtonElement>, page: number) => void;
|
|
39
|
+
/** Function called when user clicks to navigate to previous page. */
|
|
40
|
+
onPreviousClick?: (event: React.SyntheticEvent<HTMLButtonElement>, page: number) => void;
|
|
41
|
+
/** Function called when page is changed. */
|
|
42
|
+
onSetPage?: (event: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPage: number) => void;
|
|
43
|
+
/** Callback function for when file details label close button is clicked */
|
|
44
|
+
onCloseFileDetailsLabel?: (event: React.MouseEvent, fileName: string, fileId?: string | number) => void;
|
|
45
|
+
/** Props passed to file details label */
|
|
46
|
+
fileDetailsLabelProps?: Omit<FileDetailsLabelProps, 'fileName'>;
|
|
47
|
+
/** Text shown in navigation */
|
|
48
|
+
paginationContent?: string;
|
|
49
|
+
/** Navigation progress announced to assistive devices. Should state the current page/image. */
|
|
50
|
+
screenreaderText?: string;
|
|
51
|
+
}
|
|
52
|
+
declare const ImagePreview: FunctionComponent<ImagePreviewProps>;
|
|
53
|
+
export default ImagePreview;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Button, ButtonVariant, Icon, ModalBody, ModalFooter, ModalHeader, Stack, StackItem } from '@patternfly/react-core';
|
|
14
|
+
import { useState, useEffect } from 'react';
|
|
15
|
+
import { ChatbotDisplayMode } from '../Chatbot';
|
|
16
|
+
import ChatbotModal from '../ChatbotModal';
|
|
17
|
+
import FileDetailsLabel from '../FileDetailsLabel';
|
|
18
|
+
import { TrashIcon } from '@patternfly/react-icons';
|
|
19
|
+
const ImagePreview = (_a) => {
|
|
20
|
+
var { isModalOpen, displayMode = ChatbotDisplayMode.default, isCompact, className, handleModalToggle, title = 'Preview images', modalHeaderProps, modalBodyProps, images, isDisabled, onSetPage, onPreviousClick, toNextPageAriaLabel = 'Go to next image', toPreviousPageAriaLabel = 'Go to previous image', onNextClick, paginationAriaLabel, onCloseFileDetailsLabel, fileDetailsLabelProps, paginationContent, screenreaderText } = _a, props = __rest(_a, ["isModalOpen", "displayMode", "isCompact", "className", "handleModalToggle", "title", "modalHeaderProps", "modalBodyProps", "images", "isDisabled", "onSetPage", "onPreviousClick", "toNextPageAriaLabel", "toPreviousPageAriaLabel", "onNextClick", "paginationAriaLabel", "onCloseFileDetailsLabel", "fileDetailsLabelProps", "paginationContent", "screenreaderText"]);
|
|
21
|
+
const [page, setPage] = useState(1);
|
|
22
|
+
const paginationText = paginationContent || `${page}/${images.length}`;
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (images.length === 0 || page > images.length) {
|
|
25
|
+
setPage(1);
|
|
26
|
+
}
|
|
27
|
+
}, [images.length, page]);
|
|
28
|
+
const handleNewPage = (_evt, newPage) => {
|
|
29
|
+
setPage(newPage);
|
|
30
|
+
onSetPage && onSetPage(_evt, newPage);
|
|
31
|
+
};
|
|
32
|
+
return (_jsxs(ChatbotModal, Object.assign({ isOpen: isModalOpen, className: `pf-chatbot__image-preview-modal pf-chatbot__image-preview-modal--${displayMode} ${isCompact ? 'pf-m-compact' : ''} ${className ? className : ''}`, displayMode: displayMode, onClose: handleModalToggle, isCompact: isCompact }, props, { children: [_jsx(ModalHeader, Object.assign({ title: title }, modalHeaderProps)), _jsx(ModalBody, Object.assign({ className: "pf-chatbot__image-preview-body" }, modalBodyProps, { children: images.length > 0 && images[page - 1] && (_jsxs(Stack, { hasGutter: true, className: "pf-chatbot__image-preview-stack", children: [_jsx(StackItem, { children: _jsx(FileDetailsLabel, Object.assign({ fileName: images[page - 1].fileName, fileSize: images[page - 1].fileSize, hasTruncation: false, onClose: onCloseFileDetailsLabel, closeButtonIcon: _jsx(TrashIcon, {}) }, fileDetailsLabelProps)) }), _jsx(StackItem, { children: _jsx("div", { className: "pf-chatbot__image-preview-body", children: images[page - 1].image }) })] })) })), images.length > 1 && (_jsx(ModalFooter, { className: "pf-chatbot__image-preview-footer", children: _jsxs("nav", { className: `pf-chatbot__image-preview-footer-buttons`, "aria-label": paginationAriaLabel, children: [_jsx(Button, { variant: ButtonVariant.plain, isDisabled: isDisabled || page === 1, "data-action": "previous", onClick: (event) => {
|
|
33
|
+
const newPage = page > 1 ? page - 1 : 1;
|
|
34
|
+
handleNewPage(event, newPage);
|
|
35
|
+
onPreviousClick && onPreviousClick(event, newPage);
|
|
36
|
+
}, "aria-label": toPreviousPageAriaLabel, children: _jsx(Icon, { iconSize: "lg", children: _jsx("svg", { className: "pf-v6-svg", viewBox: "0 0 280 500", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: _jsx("path", { d: "M31.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L127.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L201.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34z" }) }) }) }), _jsx("span", { children: paginationText }), _jsx("div", { className: "pf-chatbot-m-hidden", "aria-live": "polite", children: screenreaderText !== null && screenreaderText !== void 0 ? screenreaderText : `Image ${page} of ${images.length}` }), _jsx(Button, { variant: ButtonVariant.plain, isDisabled: isDisabled || page === images.length, "aria-label": toNextPageAriaLabel, "data-action": "next", onClick: (event) => {
|
|
37
|
+
const newPage = page + 1 <= images.length ? page + 1 : images.length;
|
|
38
|
+
handleNewPage(event, newPage);
|
|
39
|
+
onNextClick && onNextClick(event, newPage);
|
|
40
|
+
}, children: _jsx(Icon, { isInline: true, iconSize: "lg", children: _jsx("svg", { className: "pf-v6-svg", viewBox: "0 0 180 500", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: _jsx("path", { d: "M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z" }) }) }) })] }) }))] })));
|
|
41
|
+
};
|
|
42
|
+
export default ImagePreview;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import ImagePreview from './ImagePreview';
|
|
5
|
+
import { ChatbotDisplayMode } from '../Chatbot';
|
|
6
|
+
const mockImages = [
|
|
7
|
+
{
|
|
8
|
+
fileName: 'image1.jpg',
|
|
9
|
+
fileSize: '2.5 MB',
|
|
10
|
+
image: _jsx("img", { src: "", alt: "Test image 1" })
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
fileName: 'image2.png',
|
|
14
|
+
fileSize: '1.8 MB',
|
|
15
|
+
image: _jsx("img", { src: "", alt: "Test image 2" })
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
fileName: 'image3.gif',
|
|
19
|
+
image: _jsx("img", { src: "", alt: "Test image 3" })
|
|
20
|
+
}
|
|
21
|
+
];
|
|
22
|
+
const defaultProps = {
|
|
23
|
+
isModalOpen: true,
|
|
24
|
+
handleModalToggle: jest.fn(),
|
|
25
|
+
images: mockImages
|
|
26
|
+
};
|
|
27
|
+
describe('ImagePreview', () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
jest.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
it('renders modal when isModalOpen is true', () => {
|
|
32
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps)));
|
|
33
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
it('does not render modal when isModalOpen is false', () => {
|
|
36
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { isModalOpen: false })));
|
|
37
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
it('displays custom title when provided', () => {
|
|
40
|
+
const customTitle = 'Custom image preview';
|
|
41
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { title: customTitle })));
|
|
42
|
+
expect(screen.getByRole('heading', { name: customTitle })).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
it('displays default title when no title provided', () => {
|
|
45
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps)));
|
|
46
|
+
expect(screen.getByRole('heading', { name: /Preview images/i })).toBeInTheDocument();
|
|
47
|
+
});
|
|
48
|
+
it('calls handleModalToggle when modal is closed', () => {
|
|
49
|
+
const mockHandleToggle = jest.fn();
|
|
50
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { handleModalToggle: mockHandleToggle })));
|
|
51
|
+
const closeButton = screen.getByRole('button', { name: /close/i });
|
|
52
|
+
fireEvent.click(closeButton);
|
|
53
|
+
expect(mockHandleToggle).toHaveBeenCalledTimes(1);
|
|
54
|
+
});
|
|
55
|
+
it('displays first image by default', () => {
|
|
56
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps)));
|
|
57
|
+
expect(screen.getByText('image1.jpg')).toBeInTheDocument();
|
|
58
|
+
expect(screen.getByText('2.5 MB')).toBeInTheDocument();
|
|
59
|
+
expect(screen.getByAltText('Test image 1')).toBeInTheDocument();
|
|
60
|
+
});
|
|
61
|
+
it('displays page counter correctly', () => {
|
|
62
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps)));
|
|
63
|
+
expect(screen.getByText('1/3')).toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
it('navigates to next image when next button is clicked', () => {
|
|
66
|
+
const mockOnNextClick = jest.fn();
|
|
67
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { onNextClick: mockOnNextClick })));
|
|
68
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
69
|
+
fireEvent.click(nextButton);
|
|
70
|
+
expect(mockOnNextClick).toHaveBeenCalled();
|
|
71
|
+
expect(screen.getByText('2/3')).toBeInTheDocument();
|
|
72
|
+
expect(screen.getByText('image2.png')).toBeInTheDocument();
|
|
73
|
+
});
|
|
74
|
+
it('navigates to previous image when previous button is clicked', () => {
|
|
75
|
+
const mockOnPreviousClick = jest.fn();
|
|
76
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { onPreviousClick: mockOnPreviousClick })));
|
|
77
|
+
// First go to page 2
|
|
78
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
79
|
+
fireEvent.click(nextButton);
|
|
80
|
+
// Then go back to page 1
|
|
81
|
+
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
|
|
82
|
+
fireEvent.click(previousButton);
|
|
83
|
+
expect(mockOnPreviousClick).toHaveBeenCalled();
|
|
84
|
+
expect(screen.getByText('1/3')).toBeInTheDocument();
|
|
85
|
+
});
|
|
86
|
+
it('calls onSetPage when page changes', () => {
|
|
87
|
+
const mockOnSetPage = jest.fn();
|
|
88
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { onSetPage: mockOnSetPage })));
|
|
89
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
90
|
+
fireEvent.click(nextButton);
|
|
91
|
+
expect(mockOnSetPage).toHaveBeenCalledWith(expect.any(Object), 2);
|
|
92
|
+
});
|
|
93
|
+
it('disables previous button on first page', () => {
|
|
94
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps)));
|
|
95
|
+
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
|
|
96
|
+
expect(previousButton).toBeDisabled();
|
|
97
|
+
});
|
|
98
|
+
it('disables next button on last page', () => {
|
|
99
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps)));
|
|
100
|
+
// Navigate to last page
|
|
101
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
102
|
+
fireEvent.click(nextButton); // page 2
|
|
103
|
+
fireEvent.click(nextButton); // page 3
|
|
104
|
+
expect(nextButton).toBeDisabled();
|
|
105
|
+
});
|
|
106
|
+
it('disables both navigation buttons when isDisabled is true', () => {
|
|
107
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { isDisabled: true })));
|
|
108
|
+
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
|
|
109
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
110
|
+
expect(previousButton).toBeDisabled();
|
|
111
|
+
expect(nextButton).toBeDisabled();
|
|
112
|
+
});
|
|
113
|
+
it('uses custom aria labels for pagination', () => {
|
|
114
|
+
const customLabels = {
|
|
115
|
+
paginationAriaLabel: 'Custom pagination',
|
|
116
|
+
toPreviousPageAriaLabel: 'Go to previous image',
|
|
117
|
+
toNextPageAriaLabel: 'Go to next image'
|
|
118
|
+
};
|
|
119
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, customLabels)));
|
|
120
|
+
expect(screen.getByRole('navigation', { name: 'Custom pagination' })).toBeInTheDocument();
|
|
121
|
+
expect(screen.getByRole('button', { name: 'Go to previous image' })).toBeInTheDocument();
|
|
122
|
+
expect(screen.getByRole('button', { name: 'Go to next image' })).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
it('renders with compact mode when isCompact is true', () => {
|
|
125
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { isCompact: true })));
|
|
126
|
+
const modal = screen.getByRole('dialog');
|
|
127
|
+
expect(modal).toHaveClass('pf-m-compact');
|
|
128
|
+
});
|
|
129
|
+
it('applies custom className when provided', () => {
|
|
130
|
+
const customClassName = 'custom-image-preview';
|
|
131
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { className: customClassName })));
|
|
132
|
+
const modal = screen.getByRole('dialog');
|
|
133
|
+
expect(modal).toHaveClass(customClassName);
|
|
134
|
+
});
|
|
135
|
+
it('applies display mode class correctly', () => {
|
|
136
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { displayMode: ChatbotDisplayMode.embedded })));
|
|
137
|
+
const modal = screen.getByRole('dialog');
|
|
138
|
+
expect(modal).toHaveClass('pf-chatbot__image-preview-modal--embedded');
|
|
139
|
+
});
|
|
140
|
+
it('passes additional props to ChatbotModal', () => {
|
|
141
|
+
const modalClass = 'custom-modal-class';
|
|
142
|
+
const additionalProps = {
|
|
143
|
+
'data-testid': 'modal',
|
|
144
|
+
className: modalClass
|
|
145
|
+
};
|
|
146
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, additionalProps)));
|
|
147
|
+
const modal = screen.getByTestId('modal');
|
|
148
|
+
expect(modal).toBeInTheDocument();
|
|
149
|
+
expect(modal).toBeInTheDocument();
|
|
150
|
+
expect(modal).toHaveClass(modalClass);
|
|
151
|
+
});
|
|
152
|
+
it('passes modalHeaderProps correctly', () => {
|
|
153
|
+
const headerClass = 'custom-modal-header-class';
|
|
154
|
+
const headerProps = {
|
|
155
|
+
'data-testid': 'header',
|
|
156
|
+
className: headerClass
|
|
157
|
+
};
|
|
158
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { modalHeaderProps: headerProps })));
|
|
159
|
+
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
160
|
+
expect(screen.getByTestId('header')).toHaveClass(headerClass);
|
|
161
|
+
});
|
|
162
|
+
it('passes modalBodyProps correctly', () => {
|
|
163
|
+
const bodyClass = 'custom-modal-body-class';
|
|
164
|
+
const bodyProps = {
|
|
165
|
+
'data-testid': 'body',
|
|
166
|
+
className: bodyClass
|
|
167
|
+
};
|
|
168
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { modalBodyProps: bodyProps })));
|
|
169
|
+
expect(screen.getByTestId('body')).toBeInTheDocument();
|
|
170
|
+
expect(screen.getByTestId('body')).toHaveClass(bodyClass);
|
|
171
|
+
});
|
|
172
|
+
it('handles single image without pagination', () => {
|
|
173
|
+
const singleImage = [mockImages[0]];
|
|
174
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { images: singleImage })));
|
|
175
|
+
expect(screen.queryByText('1/1')).not.toBeInTheDocument();
|
|
176
|
+
expect(screen.queryByRole('button', { name: /Go to previous image/i })).not.toBeInTheDocument();
|
|
177
|
+
expect(screen.queryByRole('button', { name: /Go to next image/i })).not.toBeInTheDocument();
|
|
178
|
+
});
|
|
179
|
+
it('calls onCloseFileDetailsLabel when file details close button is clicked', () => {
|
|
180
|
+
const mockOnClose = jest.fn();
|
|
181
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { onCloseFileDetailsLabel: mockOnClose })));
|
|
182
|
+
const closeButton = screen.getByRole('button', { name: /Close image1.jpg/i });
|
|
183
|
+
fireEvent.click(closeButton);
|
|
184
|
+
expect(mockOnClose).toHaveBeenCalled();
|
|
185
|
+
});
|
|
186
|
+
it('passes fileDetailsLabelProps correctly to FileDetailsLabel', () => {
|
|
187
|
+
const customFileDetailsProps = {
|
|
188
|
+
'data-testid': 'custom-file-details'
|
|
189
|
+
};
|
|
190
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { fileDetailsLabelProps: customFileDetailsProps })));
|
|
191
|
+
expect(screen.getByTestId('custom-file-details')).toBeInTheDocument();
|
|
192
|
+
});
|
|
193
|
+
it('displays file details for current page when navigating', () => {
|
|
194
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps)));
|
|
195
|
+
// Initially shows first image details
|
|
196
|
+
expect(screen.getByText('image1.jpg')).toBeInTheDocument();
|
|
197
|
+
expect(screen.getByText('2.5 MB')).toBeInTheDocument();
|
|
198
|
+
// Navigate to second page
|
|
199
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
200
|
+
fireEvent.click(nextButton);
|
|
201
|
+
// Should now show second image details
|
|
202
|
+
expect(screen.getByText('image2.png')).toBeInTheDocument();
|
|
203
|
+
expect(screen.getByText('1.8 MB')).toBeInTheDocument();
|
|
204
|
+
// Navigate to third page
|
|
205
|
+
fireEvent.click(nextButton);
|
|
206
|
+
// Should now show third image details (no file size)
|
|
207
|
+
expect(screen.getByText('image3.gif')).toBeInTheDocument();
|
|
208
|
+
expect(screen.queryByText(/MB/)).not.toBeInTheDocument();
|
|
209
|
+
});
|
|
210
|
+
it('sets hasTruncation to false on FileDetailsLabel', () => {
|
|
211
|
+
const longFileName = 'very-long-filename-that-would-normally-be-truncated-in-other-contexts.jpg';
|
|
212
|
+
const imageWithLongName = {
|
|
213
|
+
fileName: longFileName,
|
|
214
|
+
fileSize: '1.0 MB',
|
|
215
|
+
image: _jsx("img", { src: "", alt: "Test image with long name" })
|
|
216
|
+
};
|
|
217
|
+
render(_jsx(ImagePreview, Object.assign({}, defaultProps, { images: [imageWithLongName] })));
|
|
218
|
+
expect(screen.getByText(longFileName)).toBeInTheDocument();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -13,9 +13,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
13
13
|
// ============================================================================
|
|
14
14
|
// Chatbot Main - Message - Content - Code Block
|
|
15
15
|
// ============================================================================
|
|
16
|
-
import { useState, useRef,
|
|
16
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
17
17
|
// Import PatternFly components
|
|
18
|
-
import { CodeBlock, CodeBlockAction, CodeBlockCode, Button, Tooltip, ExpandableSection, ExpandableSectionToggle, ExpandableSectionVariant } from '@patternfly/react-core';
|
|
18
|
+
import { CodeBlock, CodeBlockAction, CodeBlockCode, Button, Tooltip, ExpandableSection, ExpandableSectionToggle, ExpandableSectionVariant, getUniqueId } from '@patternfly/react-core';
|
|
19
19
|
import { CheckIcon } from '@patternfly/react-icons/dist/esm/icons/check-icon';
|
|
20
20
|
import { CopyIcon } from '@patternfly/react-icons/dist/esm/icons/copy-icon';
|
|
21
21
|
const DEFAULT_EXPANDED_TEXT = 'Show less';
|
|
@@ -26,9 +26,9 @@ const CodeBlockMessage = (_a) => {
|
|
|
26
26
|
const [copied, setCopied] = useState(false);
|
|
27
27
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
28
28
|
const buttonRef = useRef();
|
|
29
|
-
const tooltipID =
|
|
30
|
-
const toggleId =
|
|
31
|
-
const contentId =
|
|
29
|
+
const tooltipID = getUniqueId();
|
|
30
|
+
const toggleId = getUniqueId();
|
|
31
|
+
const contentId = getUniqueId();
|
|
32
32
|
const codeBlockRef = useRef(null);
|
|
33
33
|
const language = (_b = /language-(\w+)/.exec(className || '')) === null || _b === void 0 ? void 0 : _b[1];
|
|
34
34
|
// 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;
|