@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,253 @@
|
|
|
1
|
+
import React from 'react';
|
|
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
|
+
|
|
7
|
+
const mockImages = [
|
|
8
|
+
{
|
|
9
|
+
fileName: 'image1.jpg',
|
|
10
|
+
fileSize: '2.5 MB',
|
|
11
|
+
image: <img src="" alt="Test image 1" />
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
fileName: 'image2.png',
|
|
15
|
+
fileSize: '1.8 MB',
|
|
16
|
+
image: <img src="" alt="Test image 2" />
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
fileName: 'image3.gif',
|
|
20
|
+
image: <img src="" alt="Test image 3" />
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const defaultProps = {
|
|
25
|
+
isModalOpen: true,
|
|
26
|
+
handleModalToggle: jest.fn(),
|
|
27
|
+
images: mockImages
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe('ImagePreview', () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
jest.clearAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('renders modal when isModalOpen is true', () => {
|
|
36
|
+
render(<ImagePreview {...defaultProps} />);
|
|
37
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('does not render modal when isModalOpen is false', () => {
|
|
41
|
+
render(<ImagePreview {...defaultProps} isModalOpen={false} />);
|
|
42
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('displays custom title when provided', () => {
|
|
46
|
+
const customTitle = 'Custom image preview';
|
|
47
|
+
render(<ImagePreview {...defaultProps} title={customTitle} />);
|
|
48
|
+
expect(screen.getByRole('heading', { name: customTitle })).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('displays default title when no title provided', () => {
|
|
52
|
+
render(<ImagePreview {...defaultProps} />);
|
|
53
|
+
expect(screen.getByRole('heading', { name: /Preview images/i })).toBeInTheDocument();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('calls handleModalToggle when modal is closed', () => {
|
|
57
|
+
const mockHandleToggle = jest.fn();
|
|
58
|
+
render(<ImagePreview {...defaultProps} handleModalToggle={mockHandleToggle} />);
|
|
59
|
+
const closeButton = screen.getByRole('button', { name: /close/i });
|
|
60
|
+
fireEvent.click(closeButton);
|
|
61
|
+
expect(mockHandleToggle).toHaveBeenCalledTimes(1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('displays first image by default', () => {
|
|
65
|
+
render(<ImagePreview {...defaultProps} />);
|
|
66
|
+
expect(screen.getByText('image1.jpg')).toBeInTheDocument();
|
|
67
|
+
expect(screen.getByText('2.5 MB')).toBeInTheDocument();
|
|
68
|
+
expect(screen.getByAltText('Test image 1')).toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('displays page counter correctly', () => {
|
|
72
|
+
render(<ImagePreview {...defaultProps} />);
|
|
73
|
+
expect(screen.getByText('1/3')).toBeInTheDocument();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('navigates to next image when next button is clicked', () => {
|
|
77
|
+
const mockOnNextClick = jest.fn();
|
|
78
|
+
render(<ImagePreview {...defaultProps} onNextClick={mockOnNextClick} />);
|
|
79
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
80
|
+
fireEvent.click(nextButton);
|
|
81
|
+
expect(mockOnNextClick).toHaveBeenCalled();
|
|
82
|
+
expect(screen.getByText('2/3')).toBeInTheDocument();
|
|
83
|
+
expect(screen.getByText('image2.png')).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('navigates to previous image when previous button is clicked', () => {
|
|
87
|
+
const mockOnPreviousClick = jest.fn();
|
|
88
|
+
render(<ImagePreview {...defaultProps} onPreviousClick={mockOnPreviousClick} />);
|
|
89
|
+
// First go to page 2
|
|
90
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
91
|
+
fireEvent.click(nextButton);
|
|
92
|
+
// Then go back to page 1
|
|
93
|
+
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
|
|
94
|
+
fireEvent.click(previousButton);
|
|
95
|
+
expect(mockOnPreviousClick).toHaveBeenCalled();
|
|
96
|
+
expect(screen.getByText('1/3')).toBeInTheDocument();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('calls onSetPage when page changes', () => {
|
|
100
|
+
const mockOnSetPage = jest.fn();
|
|
101
|
+
render(<ImagePreview {...defaultProps} onSetPage={mockOnSetPage} />);
|
|
102
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
103
|
+
fireEvent.click(nextButton);
|
|
104
|
+
expect(mockOnSetPage).toHaveBeenCalledWith(expect.any(Object), 2);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('disables previous button on first page', () => {
|
|
108
|
+
render(<ImagePreview {...defaultProps} />);
|
|
109
|
+
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
|
|
110
|
+
expect(previousButton).toBeDisabled();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('disables next button on last page', () => {
|
|
114
|
+
render(<ImagePreview {...defaultProps} />);
|
|
115
|
+
// Navigate to last page
|
|
116
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
117
|
+
fireEvent.click(nextButton); // page 2
|
|
118
|
+
fireEvent.click(nextButton); // page 3
|
|
119
|
+
expect(nextButton).toBeDisabled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('disables both navigation buttons when isDisabled is true', () => {
|
|
123
|
+
render(<ImagePreview {...defaultProps} isDisabled={true} />);
|
|
124
|
+
const previousButton = screen.getByRole('button', { name: /Go to previous image/i });
|
|
125
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
126
|
+
expect(previousButton).toBeDisabled();
|
|
127
|
+
expect(nextButton).toBeDisabled();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('uses custom aria labels for pagination', () => {
|
|
131
|
+
const customLabels = {
|
|
132
|
+
paginationAriaLabel: 'Custom pagination',
|
|
133
|
+
toPreviousPageAriaLabel: 'Go to previous image',
|
|
134
|
+
toNextPageAriaLabel: 'Go to next image'
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
render(<ImagePreview {...defaultProps} {...customLabels} />);
|
|
138
|
+
expect(screen.getByRole('navigation', { name: 'Custom pagination' })).toBeInTheDocument();
|
|
139
|
+
expect(screen.getByRole('button', { name: 'Go to previous image' })).toBeInTheDocument();
|
|
140
|
+
expect(screen.getByRole('button', { name: 'Go to next image' })).toBeInTheDocument();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('renders with compact mode when isCompact is true', () => {
|
|
144
|
+
render(<ImagePreview {...defaultProps} isCompact={true} />);
|
|
145
|
+
const modal = screen.getByRole('dialog');
|
|
146
|
+
expect(modal).toHaveClass('pf-m-compact');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('applies custom className when provided', () => {
|
|
150
|
+
const customClassName = 'custom-image-preview';
|
|
151
|
+
render(<ImagePreview {...defaultProps} className={customClassName} />);
|
|
152
|
+
const modal = screen.getByRole('dialog');
|
|
153
|
+
expect(modal).toHaveClass(customClassName);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('applies display mode class correctly', () => {
|
|
157
|
+
render(<ImagePreview {...defaultProps} displayMode={ChatbotDisplayMode.embedded} />);
|
|
158
|
+
const modal = screen.getByRole('dialog');
|
|
159
|
+
expect(modal).toHaveClass('pf-chatbot__image-preview-modal--embedded');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('passes additional props to ChatbotModal', () => {
|
|
163
|
+
const modalClass = 'custom-modal-class';
|
|
164
|
+
const additionalProps = {
|
|
165
|
+
'data-testid': 'modal',
|
|
166
|
+
className: modalClass
|
|
167
|
+
};
|
|
168
|
+
render(<ImagePreview {...defaultProps} {...additionalProps} />);
|
|
169
|
+
const modal = screen.getByTestId('modal');
|
|
170
|
+
expect(modal).toBeInTheDocument();
|
|
171
|
+
expect(modal).toBeInTheDocument();
|
|
172
|
+
expect(modal).toHaveClass(modalClass);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('passes modalHeaderProps correctly', () => {
|
|
176
|
+
const headerClass = 'custom-modal-header-class';
|
|
177
|
+
const headerProps = {
|
|
178
|
+
'data-testid': 'header',
|
|
179
|
+
className: headerClass
|
|
180
|
+
};
|
|
181
|
+
render(<ImagePreview {...defaultProps} modalHeaderProps={headerProps} />);
|
|
182
|
+
expect(screen.getByTestId('header')).toBeInTheDocument();
|
|
183
|
+
expect(screen.getByTestId('header')).toHaveClass(headerClass);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('passes modalBodyProps correctly', () => {
|
|
187
|
+
const bodyClass = 'custom-modal-body-class';
|
|
188
|
+
const bodyProps = {
|
|
189
|
+
'data-testid': 'body',
|
|
190
|
+
className: bodyClass
|
|
191
|
+
};
|
|
192
|
+
render(<ImagePreview {...defaultProps} modalBodyProps={bodyProps} />);
|
|
193
|
+
expect(screen.getByTestId('body')).toBeInTheDocument();
|
|
194
|
+
expect(screen.getByTestId('body')).toHaveClass(bodyClass);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('handles single image without pagination', () => {
|
|
198
|
+
const singleImage = [mockImages[0]];
|
|
199
|
+
render(<ImagePreview {...defaultProps} images={singleImage} />);
|
|
200
|
+
expect(screen.queryByText('1/1')).not.toBeInTheDocument();
|
|
201
|
+
expect(screen.queryByRole('button', { name: /Go to previous image/i })).not.toBeInTheDocument();
|
|
202
|
+
expect(screen.queryByRole('button', { name: /Go to next image/i })).not.toBeInTheDocument();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('calls onCloseFileDetailsLabel when file details close button is clicked', () => {
|
|
206
|
+
const mockOnClose = jest.fn();
|
|
207
|
+
render(<ImagePreview {...defaultProps} onCloseFileDetailsLabel={mockOnClose} />);
|
|
208
|
+
const closeButton = screen.getByRole('button', { name: /Close image1.jpg/i });
|
|
209
|
+
fireEvent.click(closeButton);
|
|
210
|
+
expect(mockOnClose).toHaveBeenCalled();
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('passes fileDetailsLabelProps correctly to FileDetailsLabel', () => {
|
|
214
|
+
const customFileDetailsProps = {
|
|
215
|
+
'data-testid': 'custom-file-details'
|
|
216
|
+
};
|
|
217
|
+
render(<ImagePreview {...defaultProps} fileDetailsLabelProps={customFileDetailsProps as any} />);
|
|
218
|
+
expect(screen.getByTestId('custom-file-details')).toBeInTheDocument();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('displays file details for current page when navigating', () => {
|
|
222
|
+
render(<ImagePreview {...defaultProps} />);
|
|
223
|
+
// Initially shows first image details
|
|
224
|
+
expect(screen.getByText('image1.jpg')).toBeInTheDocument();
|
|
225
|
+
expect(screen.getByText('2.5 MB')).toBeInTheDocument();
|
|
226
|
+
|
|
227
|
+
// Navigate to second page
|
|
228
|
+
const nextButton = screen.getByRole('button', { name: /Go to next image/i });
|
|
229
|
+
fireEvent.click(nextButton);
|
|
230
|
+
|
|
231
|
+
// Should now show second image details
|
|
232
|
+
expect(screen.getByText('image2.png')).toBeInTheDocument();
|
|
233
|
+
expect(screen.getByText('1.8 MB')).toBeInTheDocument();
|
|
234
|
+
|
|
235
|
+
// Navigate to third page
|
|
236
|
+
fireEvent.click(nextButton);
|
|
237
|
+
|
|
238
|
+
// Should now show third image details (no file size)
|
|
239
|
+
expect(screen.getByText('image3.gif')).toBeInTheDocument();
|
|
240
|
+
expect(screen.queryByText(/MB/)).not.toBeInTheDocument();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('sets hasTruncation to false on FileDetailsLabel', () => {
|
|
244
|
+
const longFileName = 'very-long-filename-that-would-normally-be-truncated-in-other-contexts.jpg';
|
|
245
|
+
const imageWithLongName = {
|
|
246
|
+
fileName: longFileName,
|
|
247
|
+
fileSize: '1.0 MB',
|
|
248
|
+
image: <img src="" alt="Test image with long name" />
|
|
249
|
+
};
|
|
250
|
+
render(<ImagePreview {...defaultProps} images={[imageWithLongName]} />);
|
|
251
|
+
expect(screen.getByText(longFileName)).toBeInTheDocument();
|
|
252
|
+
});
|
|
253
|
+
});
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
ButtonVariant,
|
|
4
|
+
Icon,
|
|
5
|
+
ModalBody,
|
|
6
|
+
ModalBodyProps,
|
|
7
|
+
ModalFooter,
|
|
8
|
+
ModalHeader,
|
|
9
|
+
ModalHeaderProps,
|
|
10
|
+
Stack,
|
|
11
|
+
StackItem
|
|
12
|
+
} from '@patternfly/react-core';
|
|
13
|
+
import {
|
|
14
|
+
useState,
|
|
15
|
+
useEffect,
|
|
16
|
+
type FunctionComponent,
|
|
17
|
+
MouseEvent as ReactMouseEvent,
|
|
18
|
+
KeyboardEvent as ReactKeyboardEvent
|
|
19
|
+
} from 'react';
|
|
20
|
+
import { ChatbotDisplayMode } from '../Chatbot';
|
|
21
|
+
import ChatbotModal, { ChatbotModalProps } from '../ChatbotModal';
|
|
22
|
+
import FileDetailsLabel, { FileDetailsLabelProps } from '../FileDetailsLabel';
|
|
23
|
+
import { TrashIcon } from '@patternfly/react-icons';
|
|
24
|
+
|
|
25
|
+
export interface ImagePreviewProps extends Omit<ChatbotModalProps, 'children'> {
|
|
26
|
+
/** Class applied to modal */
|
|
27
|
+
className?: string;
|
|
28
|
+
/** Function that handles modal toggle */
|
|
29
|
+
handleModalToggle: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void;
|
|
30
|
+
/** Whether modal is open */
|
|
31
|
+
isModalOpen: boolean;
|
|
32
|
+
/** Title of modal */
|
|
33
|
+
title?: string;
|
|
34
|
+
/** Display mode for the Chatbot parent; this influences the styles applied */
|
|
35
|
+
displayMode?: ChatbotDisplayMode;
|
|
36
|
+
/** Sets modal to compact styling. */
|
|
37
|
+
isCompact?: boolean;
|
|
38
|
+
/** Additional props passed to modal header */
|
|
39
|
+
modalHeaderProps?: ModalHeaderProps;
|
|
40
|
+
/** Additional props passed to modal body */
|
|
41
|
+
modalBodyProps?: ModalBodyProps;
|
|
42
|
+
/** Images displayed in modal */
|
|
43
|
+
images: { fileName: string; fileSize?: string; image: React.ReactNode }[];
|
|
44
|
+
/** Flag indicating if the pagination is disabled. */
|
|
45
|
+
isDisabled?: boolean;
|
|
46
|
+
/** Accessible label for the pagination component. */
|
|
47
|
+
paginationAriaLabel?: string;
|
|
48
|
+
/** Accessible label for the button which moves to the next page. */
|
|
49
|
+
toNextPageAriaLabel?: string;
|
|
50
|
+
/** Accessible label for the button which moves to the previous page. */
|
|
51
|
+
toPreviousPageAriaLabel?: string;
|
|
52
|
+
/** Function called when user clicks to navigate to next page. */
|
|
53
|
+
onNextClick?: (event: React.SyntheticEvent<HTMLButtonElement>, page: number) => void;
|
|
54
|
+
/** Function called when user clicks to navigate to previous page. */
|
|
55
|
+
onPreviousClick?: (event: React.SyntheticEvent<HTMLButtonElement>, page: number) => void;
|
|
56
|
+
/** Function called when page is changed. */
|
|
57
|
+
onSetPage?: (event: React.MouseEvent | React.KeyboardEvent | MouseEvent, newPage: number) => void;
|
|
58
|
+
/** Callback function for when file details label close button is clicked */
|
|
59
|
+
onCloseFileDetailsLabel?: (event: React.MouseEvent, fileName: string, fileId?: string | number) => void;
|
|
60
|
+
/** Props passed to file details label */
|
|
61
|
+
fileDetailsLabelProps?: Omit<FileDetailsLabelProps, 'fileName'>;
|
|
62
|
+
/** Text shown in navigation */
|
|
63
|
+
paginationContent?: string;
|
|
64
|
+
/** Navigation progress announced to assistive devices. Should state the current page/image. */
|
|
65
|
+
screenreaderText?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const ImagePreview: FunctionComponent<ImagePreviewProps> = ({
|
|
69
|
+
isModalOpen,
|
|
70
|
+
displayMode = ChatbotDisplayMode.default,
|
|
71
|
+
isCompact,
|
|
72
|
+
className,
|
|
73
|
+
handleModalToggle,
|
|
74
|
+
title = 'Preview images',
|
|
75
|
+
modalHeaderProps,
|
|
76
|
+
modalBodyProps,
|
|
77
|
+
images,
|
|
78
|
+
isDisabled,
|
|
79
|
+
onSetPage,
|
|
80
|
+
onPreviousClick,
|
|
81
|
+
toNextPageAriaLabel = 'Go to next image',
|
|
82
|
+
toPreviousPageAriaLabel = 'Go to previous image',
|
|
83
|
+
onNextClick,
|
|
84
|
+
paginationAriaLabel,
|
|
85
|
+
onCloseFileDetailsLabel,
|
|
86
|
+
fileDetailsLabelProps,
|
|
87
|
+
paginationContent,
|
|
88
|
+
screenreaderText,
|
|
89
|
+
...props
|
|
90
|
+
}: ImagePreviewProps) => {
|
|
91
|
+
const [page, setPage] = useState(1);
|
|
92
|
+
const paginationText = paginationContent || `${page}/${images.length}`;
|
|
93
|
+
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (images.length === 0 || page > images.length) {
|
|
96
|
+
setPage(1);
|
|
97
|
+
}
|
|
98
|
+
}, [images.length, page]);
|
|
99
|
+
|
|
100
|
+
const handleNewPage = (_evt: ReactMouseEvent | ReactKeyboardEvent | MouseEvent, newPage: number) => {
|
|
101
|
+
setPage(newPage);
|
|
102
|
+
onSetPage && onSetPage(_evt, newPage);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<ChatbotModal
|
|
107
|
+
isOpen={isModalOpen}
|
|
108
|
+
className={`pf-chatbot__image-preview-modal pf-chatbot__image-preview-modal--${displayMode} ${isCompact ? 'pf-m-compact' : ''} ${className ? className : ''}`}
|
|
109
|
+
displayMode={displayMode}
|
|
110
|
+
onClose={handleModalToggle}
|
|
111
|
+
isCompact={isCompact}
|
|
112
|
+
{...props}
|
|
113
|
+
>
|
|
114
|
+
<ModalHeader title={title} {...modalHeaderProps} />
|
|
115
|
+
<ModalBody className="pf-chatbot__image-preview-body" {...modalBodyProps}>
|
|
116
|
+
{images.length > 0 && images[page - 1] && (
|
|
117
|
+
<Stack hasGutter className="pf-chatbot__image-preview-stack">
|
|
118
|
+
<StackItem>
|
|
119
|
+
<FileDetailsLabel
|
|
120
|
+
fileName={images[page - 1].fileName}
|
|
121
|
+
fileSize={images[page - 1].fileSize}
|
|
122
|
+
hasTruncation={false}
|
|
123
|
+
onClose={onCloseFileDetailsLabel}
|
|
124
|
+
closeButtonIcon={<TrashIcon />}
|
|
125
|
+
{...fileDetailsLabelProps}
|
|
126
|
+
/>
|
|
127
|
+
</StackItem>
|
|
128
|
+
<StackItem>
|
|
129
|
+
<div className="pf-chatbot__image-preview-body">{images[page - 1].image}</div>
|
|
130
|
+
</StackItem>
|
|
131
|
+
</Stack>
|
|
132
|
+
)}
|
|
133
|
+
</ModalBody>
|
|
134
|
+
{images.length > 1 && (
|
|
135
|
+
<ModalFooter className="pf-chatbot__image-preview-footer">
|
|
136
|
+
<nav className={`pf-chatbot__image-preview-footer-buttons`} aria-label={paginationAriaLabel}>
|
|
137
|
+
<Button
|
|
138
|
+
variant={ButtonVariant.plain}
|
|
139
|
+
isDisabled={isDisabled || page === 1}
|
|
140
|
+
data-action="previous"
|
|
141
|
+
onClick={(event) => {
|
|
142
|
+
const newPage = page > 1 ? page - 1 : 1;
|
|
143
|
+
handleNewPage(event, newPage);
|
|
144
|
+
onPreviousClick && onPreviousClick(event, newPage);
|
|
145
|
+
}}
|
|
146
|
+
aria-label={toPreviousPageAriaLabel}
|
|
147
|
+
>
|
|
148
|
+
<Icon iconSize="lg">
|
|
149
|
+
{/* these are inline because the viewBox that works in a round icon is different than the PatternFly default */}
|
|
150
|
+
<svg
|
|
151
|
+
className="pf-v6-svg"
|
|
152
|
+
viewBox="0 0 280 500"
|
|
153
|
+
fill="currentColor"
|
|
154
|
+
aria-hidden="true"
|
|
155
|
+
role="img"
|
|
156
|
+
width="1em"
|
|
157
|
+
height="1em"
|
|
158
|
+
>
|
|
159
|
+
<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"></path>
|
|
160
|
+
</svg>
|
|
161
|
+
</Icon>
|
|
162
|
+
</Button>
|
|
163
|
+
<span>{paginationText}</span>
|
|
164
|
+
<div className="pf-chatbot-m-hidden" aria-live="polite">
|
|
165
|
+
{screenreaderText ?? `Image ${page} of ${images.length}`}
|
|
166
|
+
</div>
|
|
167
|
+
<Button
|
|
168
|
+
variant={ButtonVariant.plain}
|
|
169
|
+
isDisabled={isDisabled || page === images.length}
|
|
170
|
+
aria-label={toNextPageAriaLabel}
|
|
171
|
+
data-action="next"
|
|
172
|
+
onClick={(event) => {
|
|
173
|
+
const newPage = page + 1 <= images.length ? page + 1 : images.length;
|
|
174
|
+
handleNewPage(event, newPage);
|
|
175
|
+
onNextClick && onNextClick(event, newPage);
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
<Icon isInline iconSize="lg">
|
|
179
|
+
{/* these are inline because the viewBox that works in a round icon is different than the PatternFly default */}
|
|
180
|
+
<svg
|
|
181
|
+
className="pf-v6-svg"
|
|
182
|
+
viewBox="0 0 180 500"
|
|
183
|
+
fill="currentColor"
|
|
184
|
+
aria-hidden="true"
|
|
185
|
+
role="img"
|
|
186
|
+
width="1em"
|
|
187
|
+
height="1em"
|
|
188
|
+
>
|
|
189
|
+
<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"></path>
|
|
190
|
+
</svg>
|
|
191
|
+
</Icon>
|
|
192
|
+
</Button>
|
|
193
|
+
</nav>
|
|
194
|
+
</ModalFooter>
|
|
195
|
+
)}
|
|
196
|
+
</ChatbotModal>
|
|
197
|
+
);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export default ImagePreview;
|
|
@@ -77,8 +77,9 @@
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
.pf-chatbot__message-inline-code {
|
|
80
|
+
--pf-chatbot-message-text-inline-code-font-size: var(--pf-t--global--font--size--body--default);
|
|
80
81
|
background-color: var(--pf-t--global--background--color--tertiary--default);
|
|
81
|
-
font-size: var(--pf-
|
|
82
|
+
font-size: var(--pf-chatbot-message-text-inline-code-font-size);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
.pf-chatbot__message-code-toggle {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ============================================================================
|
|
2
2
|
// Chatbot Main - Message - Content - Code Block
|
|
3
3
|
// ============================================================================
|
|
4
|
-
import { useState, useRef,
|
|
4
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
5
5
|
// Import PatternFly components
|
|
6
6
|
import {
|
|
7
7
|
CodeBlock,
|
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
ExpandableSectionToggle,
|
|
14
14
|
ExpandableSectionProps,
|
|
15
15
|
ExpandableSectionToggleProps,
|
|
16
|
-
ExpandableSectionVariant
|
|
16
|
+
ExpandableSectionVariant,
|
|
17
|
+
getUniqueId
|
|
17
18
|
} from '@patternfly/react-core';
|
|
18
19
|
|
|
19
20
|
import { CheckIcon } from '@patternfly/react-icons/dist/esm/icons/check-icon';
|
|
@@ -56,9 +57,9 @@ const CodeBlockMessage = ({
|
|
|
56
57
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
57
58
|
|
|
58
59
|
const buttonRef = useRef();
|
|
59
|
-
const tooltipID =
|
|
60
|
-
const toggleId =
|
|
61
|
-
const contentId =
|
|
60
|
+
const tooltipID = getUniqueId();
|
|
61
|
+
const toggleId = getUniqueId();
|
|
62
|
+
const contentId = getUniqueId();
|
|
62
63
|
const codeBlockRef = useRef<HTMLDivElement>(null);
|
|
63
64
|
|
|
64
65
|
const language = /language-(\w+)/.exec(className || '')?.[1];
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import { Button, ButtonProps } from '@patternfly/react-core';
|
|
6
6
|
import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
|
|
7
|
+
import { ExtraProps } from 'react-markdown';
|
|
7
8
|
|
|
8
|
-
const LinkMessage = ({ children, target, href, ...props }: ButtonProps) => {
|
|
9
|
+
const LinkMessage = ({ children, target, href, id, ...props }: ButtonProps & ExtraProps) => {
|
|
9
10
|
if (target === '_blank') {
|
|
10
11
|
return (
|
|
11
12
|
<Button
|
|
@@ -16,6 +17,8 @@ const LinkMessage = ({ children, target, href, ...props }: ButtonProps) => {
|
|
|
16
17
|
iconPosition="end"
|
|
17
18
|
isInline
|
|
18
19
|
target={target}
|
|
20
|
+
// need to explicitly call this out or id doesn't seem to get passed - required for footnotes
|
|
21
|
+
id={id}
|
|
19
22
|
{...props}
|
|
20
23
|
>
|
|
21
24
|
{children}
|
|
@@ -24,7 +27,8 @@ const LinkMessage = ({ children, target, href, ...props }: ButtonProps) => {
|
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
return (
|
|
27
|
-
|
|
30
|
+
// need to explicitly call this out or id doesn't seem to get passed - required for footnotes
|
|
31
|
+
<Button isInline component="a" href={href} variant="link" id={id} {...props}>
|
|
28
32
|
{children}
|
|
29
33
|
</Button>
|
|
30
34
|
);
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
import { ExtraProps } from 'react-markdown';
|
|
6
6
|
import { ListItem } from '@patternfly/react-core';
|
|
7
7
|
|
|
8
|
-
const ListItemMessage = ({ children }: JSX.IntrinsicElements['li'] & ExtraProps) =>
|
|
8
|
+
const ListItemMessage = ({ children, ...props }: JSX.IntrinsicElements['li'] & ExtraProps) => (
|
|
9
|
+
<ListItem {...props} tabIndex={props?.id?.includes('fn-') ? -1 : props?.tabIndex}>
|
|
10
|
+
{children}
|
|
11
|
+
</ListItem>
|
|
12
|
+
);
|
|
9
13
|
|
|
10
14
|
export default ListItemMessage;
|
|
@@ -21,5 +21,22 @@
|
|
|
21
21
|
background-color: var(--pf-t--global--color--brand--default);
|
|
22
22
|
color: var(--pf-t--global--text--color--on-brand--default);
|
|
23
23
|
padding: var(--pf-t--global--spacer--sm);
|
|
24
|
+
|
|
25
|
+
// prevents issues when highlighting things like footnotes - don't have blue on blue
|
|
26
|
+
.pf-chatbot__message-text {
|
|
27
|
+
background-color: initial;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// targets footnotes specifically and prevents misalignment problems
|
|
32
|
+
.footnotes {
|
|
33
|
+
li > span {
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
li a {
|
|
40
|
+
color: var(--pf-t--global--text--color--on-brand--default);
|
|
24
41
|
}
|
|
25
42
|
}
|
package/src/Message/Message.scss
CHANGED
|
@@ -89,6 +89,49 @@
|
|
|
89
89
|
display: grid;
|
|
90
90
|
gap: var(--pf-t--global--spacer--sm);
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
// targets footnotes specifically
|
|
94
|
+
.footnotes,
|
|
95
|
+
.pf-chatbot__message-text.footnotes {
|
|
96
|
+
padding: var(--pf-t--global--spacer--sm) var(--pf-t--global--spacer--sm) 0 var(--pf-t--global--spacer--sm);
|
|
97
|
+
--pf-chatbot-message-text-font-size: var(--pf-t--global--font--size--xs);
|
|
98
|
+
--pf-chatbot-message-text-inline-code-font-size: var(--pf-t--global--font--size--xs);
|
|
99
|
+
|
|
100
|
+
.pf-chatbot__message-text h1,
|
|
101
|
+
h2,
|
|
102
|
+
h3,
|
|
103
|
+
h4,
|
|
104
|
+
h5,
|
|
105
|
+
h6 {
|
|
106
|
+
--pf-v6-c-content--h1--FontSize: var(--pf-t--global--font--size--md);
|
|
107
|
+
--pf-v6-c-content--h2--FontSize: var(--pf-t--global--font--size--md);
|
|
108
|
+
--pf-v6-c-content--h3--FontSize: var(--pf-t--global--font--size--md);
|
|
109
|
+
--pf-v6-c-content--h4--FontSize: var(--pf-t--global--font--size--md);
|
|
110
|
+
--pf-v6-c-content--h5--FontSize: var(--pf-t--global--font--size--md);
|
|
111
|
+
--pf-v6-c-content--h6--FontSize: var(--pf-t--global--font--size--md);
|
|
112
|
+
}
|
|
113
|
+
.pf-chatbot__message-text .pf-v6-c-content,
|
|
114
|
+
.pf-chatbot__message-text .pf-v6-c-content--small,
|
|
115
|
+
.pf-chatbot__message-text .pf-v6-c-content--blockquote,
|
|
116
|
+
.pf-chatbot__message-text p,
|
|
117
|
+
.pf-chatbot__message-text a {
|
|
118
|
+
--pf-v6-c-content--FontSize: var(--pf-t--global--font--size--xs);
|
|
119
|
+
}
|
|
120
|
+
.pf-chatbot__message-inline-code,
|
|
121
|
+
.pf-chatbot__message-text .pf-v6-c-button.pf-m-link,
|
|
122
|
+
.pf-chatbot__message-ordered-list .pf-v6-c-list,
|
|
123
|
+
.pf-chatbot__message-ordered-list ul,
|
|
124
|
+
.pf-chatbot__message-ordered-list li,
|
|
125
|
+
.pf-chatbot__message-unordered-list .pf-v6-c-list,
|
|
126
|
+
.pf-chatbot__message-unordered-list ul,
|
|
127
|
+
.pf-chatbot__message-unordered-list li {
|
|
128
|
+
font-size: var(--pf-t--global--font--size--xs);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.footnotes {
|
|
133
|
+
background-color: var(--pf-t--global--background--color--tertiary--default);
|
|
134
|
+
}
|
|
92
135
|
}
|
|
93
136
|
|
|
94
137
|
// Attachments
|
|
@@ -106,6 +149,7 @@
|
|
|
106
149
|
@import './MessageLoading';
|
|
107
150
|
@import './CodeBlockMessage/CodeBlockMessage';
|
|
108
151
|
@import './TextMessage/TextMessage';
|
|
152
|
+
@import './SuperscriptMessage/SuperscriptMessage.scss';
|
|
109
153
|
|
|
110
154
|
// ============================================================================
|
|
111
155
|
// Information density styles
|