@patternfly/chatbot 6.3.1 → 6.4.0-prerelease.10
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/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +8 -14
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +53 -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/FileDropZone/FileDropZone.d.ts +1 -2
- package/dist/cjs/Message/Message.d.ts +9 -2
- package/dist/cjs/Message/Message.js +40 -34
- package/dist/cjs/Message/Message.test.js +37 -0
- package/dist/cjs/Message/MessageInput.d.ts +3 -1
- package/dist/cjs/Message/MessageInput.js +2 -2
- package/dist/cjs/MessageBar/AttachButton.d.ts +2 -2
- package/dist/cjs/MessageBar/MessageBar.d.ts +2 -2
- 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/MessageDivider/MessageDivider.d.ts +9 -0
- package/dist/cjs/MessageDivider/MessageDivider.js +23 -0
- package/dist/cjs/MessageDivider/MessageDivider.test.d.ts +1 -0
- package/dist/cjs/MessageDivider/MessageDivider.test.js +29 -0
- package/dist/cjs/MessageDivider/index.d.ts +2 -0
- package/dist/cjs/MessageDivider/index.js +23 -0
- package/dist/cjs/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/cjs/ResponseActions/ResponseActions.js +4 -4
- package/dist/cjs/ResponseActions/ResponseActions.test.js +6 -1
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/css/main.css +103 -81
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/MessageDivider/package.json +1 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +2 -0
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +27 -4
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +10 -16
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +54 -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/FileDropZone/FileDropZone.d.ts +1 -2
- package/dist/esm/Message/Message.d.ts +9 -2
- package/dist/esm/Message/Message.js +40 -34
- package/dist/esm/Message/Message.test.js +37 -0
- package/dist/esm/Message/MessageInput.d.ts +3 -1
- package/dist/esm/Message/MessageInput.js +2 -2
- package/dist/esm/MessageBar/AttachButton.d.ts +2 -2
- package/dist/esm/MessageBar/MessageBar.d.ts +2 -2
- 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/MessageDivider/MessageDivider.d.ts +9 -0
- package/dist/esm/MessageDivider/MessageDivider.js +21 -0
- package/dist/esm/MessageDivider/MessageDivider.test.d.ts +1 -0
- package/dist/esm/MessageDivider/MessageDivider.test.js +24 -0
- package/dist/esm/MessageDivider/index.d.ts +2 -0
- package/dist/esm/MessageDivider/index.js +2 -0
- package/dist/esm/ResponseActions/ResponseActions.d.ts +1 -0
- package/dist/esm/ResponseActions/ResponseActions.js +5 -5
- package/dist/esm/ResponseActions/ResponseActions.test.js +6 -1
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -4
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithDividers.tsx +24 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +15 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +39 -7
- 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 +45 -5
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +206 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +30 -4
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +33 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotDisplayMode.tsx +486 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotTranscripts.tsx +565 -0
- package/src/Chatbot/Chatbot.scss +1 -1
- package/src/ChatbotContent/ChatbotContent.scss +1 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx +6 -6
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +5 -2
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +70 -32
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +176 -3
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +110 -60
- package/src/ChatbotFooter/ChatbotFooter.scss +1 -1
- package/src/ChatbotHeader/ChatbotHeader.scss +3 -3
- 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/ChatbotToggle/ChatbotToggle.scss +2 -2
- package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +6 -9
- package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +6 -9
- package/src/FileDropZone/FileDropZone.tsx +2 -2
- package/src/Message/Message.scss +9 -7
- package/src/Message/Message.test.tsx +54 -0
- package/src/Message/Message.tsx +70 -50
- package/src/Message/MessageInput.tsx +5 -1
- package/src/MessageBar/AttachButton.tsx +2 -2
- package/src/MessageBar/MessageBar.tsx +25 -5
- package/src/MessageBar/SendButton.scss +3 -3
- package/src/MessageBox/JumpButton.scss +1 -1
- package/src/MessageBox/JumpButton.test.tsx +4 -4
- package/src/MessageBox/JumpButton.tsx +20 -4
- package/src/MessageBox/MessageBox.test.tsx +2 -2
- package/src/MessageBox/MessageBox.tsx +23 -2
- package/src/MessageDivider/MessageDivider.scss +45 -0
- package/src/MessageDivider/MessageDivider.test.tsx +24 -0
- package/src/MessageDivider/MessageDivider.tsx +35 -0
- package/src/MessageDivider/index.ts +3 -0
- package/src/ResponseActions/ResponseActions.test.tsx +6 -1
- package/src/ResponseActions/ResponseActions.tsx +24 -3
- package/src/index.ts +3 -0
- package/src/main.scss +1 -52
|
@@ -13,7 +13,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
|
13
13
|
import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
|
|
14
14
|
import ChatbotConversationHistoryNav from './ChatbotConversationHistoryNav';
|
|
15
15
|
import { EmptyStateStatus, Spinner } from '@patternfly/react-core';
|
|
16
|
-
import { OutlinedCommentsIcon, SearchIcon } from '@patternfly/react-icons';
|
|
16
|
+
import { BellIcon, OutlinedCommentsIcon, SearchIcon } from '@patternfly/react-icons';
|
|
17
17
|
const ERROR = {
|
|
18
18
|
bodyText: (_jsxs(_Fragment, { children: ["To try again, check your connection and reload this page. If the issue persists,", ' ', _jsx("a", { href: "", children: "contact the support team" }), "."] })),
|
|
19
19
|
buttonText: 'Reload',
|
|
@@ -65,6 +65,10 @@ describe('ChatbotConversationHistoryNav', () => {
|
|
|
65
65
|
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), reverseButtonOrder: true, conversations: initialConversations }));
|
|
66
66
|
expect(screen.getByTestId('chatbot-nav-drawer-actions')).toHaveClass('pf-v6-c-drawer__actions--reversed');
|
|
67
67
|
});
|
|
68
|
+
it('should disable new chat button', () => {
|
|
69
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), reverseButtonOrder: true, conversations: initialConversations, newChatButtonProps: { isDisabled: true }, onNewChat: jest.fn() }));
|
|
70
|
+
expect(screen.getByRole('button', { name: 'New chat' })).toBeDisabled();
|
|
71
|
+
});
|
|
68
72
|
it('should not apply the reversed class when reverseButtonOrder is false', () => {
|
|
69
73
|
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), reverseButtonOrder: false, conversations: initialConversations }));
|
|
70
74
|
expect(screen.getByTestId('chatbot-nav-drawer-actions')).not.toHaveClass('pf-v6-c-drawer__actions--reversed');
|
|
@@ -143,7 +147,7 @@ describe('ChatbotConversationHistoryNav', () => {
|
|
|
143
147
|
})).toBeTruthy();
|
|
144
148
|
expect(screen.getByRole('button', { name: /Close drawer panel/i })).toBeTruthy();
|
|
145
149
|
expect(screen.getByRole('button', { name: /Loading... Reload/i })).toBeTruthy();
|
|
146
|
-
expect(screen.getByRole('textbox', { name: /
|
|
150
|
+
expect(screen.getByRole('textbox', { name: /Search previous conversations/i })).toBeTruthy();
|
|
147
151
|
expect(screen.getByRole('heading', { name: /Could not load chat history/i })).toBeTruthy();
|
|
148
152
|
});
|
|
149
153
|
it('should accept errorState without button', () => {
|
|
@@ -153,7 +157,7 @@ describe('ChatbotConversationHistoryNav', () => {
|
|
|
153
157
|
})).toBeTruthy();
|
|
154
158
|
expect(screen.getByRole('button', { name: /Close drawer panel/i })).toBeTruthy();
|
|
155
159
|
expect(screen.queryByRole('button', { name: /Loading... Reload/i })).toBeFalsy();
|
|
156
|
-
expect(screen.getByRole('textbox', { name: /
|
|
160
|
+
expect(screen.getByRole('textbox', { name: /Search previous conversations/i })).toBeTruthy();
|
|
157
161
|
expect(screen.getByRole('heading', { name: /Could not load chat history/i })).toBeTruthy();
|
|
158
162
|
});
|
|
159
163
|
it('should show loading state over error state if both are supplied', () => {
|
|
@@ -176,4 +180,51 @@ describe('ChatbotConversationHistoryNav', () => {
|
|
|
176
180
|
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), reverseButtonOrder: false, handleTextInputChange: jest.fn(), conversations: initialConversations, noResultsState: NO_RESULTS, isCompact: true, "data-testid": "drawer" }));
|
|
177
181
|
expect(screen.getByTestId('drawer')).toHaveClass('pf-m-compact');
|
|
178
182
|
});
|
|
183
|
+
it('should display the default title', () => {
|
|
184
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations }));
|
|
185
|
+
expect(screen.getByText('Chat history')).toBeInTheDocument();
|
|
186
|
+
});
|
|
187
|
+
it('should display the custom title', () => {
|
|
188
|
+
render(_jsx(ChatbotConversationHistoryNav, { title: "PatternFly history", onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations }));
|
|
189
|
+
expect(screen.getByText('PatternFly history')).toBeInTheDocument();
|
|
190
|
+
});
|
|
191
|
+
it('should display the clock icon', () => {
|
|
192
|
+
const { container } = render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations }));
|
|
193
|
+
const iconElement = container.querySelector('.pf-chatbot__title-icon');
|
|
194
|
+
expect(iconElement).toBeInTheDocument();
|
|
195
|
+
});
|
|
196
|
+
it('Passes listTitleProps to Title', () => {
|
|
197
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations }, listTitleProps: { className: 'test' } }));
|
|
198
|
+
expect(screen.getByRole('heading', { name: /Today/i })).toHaveClass('test');
|
|
199
|
+
});
|
|
200
|
+
it('Overrides list title heading level when titleProps.headingLevel is passed', () => {
|
|
201
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations }, listTitleProps: { headingLevel: 'h2' } }));
|
|
202
|
+
expect(screen.queryByRole('heading', { name: /Today/i, level: 4 })).not.toBeInTheDocument();
|
|
203
|
+
expect(screen.getByRole('heading', { name: /Today/i, level: 2 })).toBeInTheDocument();
|
|
204
|
+
});
|
|
205
|
+
it('Passes listProps to List when conversations is an array', () => {
|
|
206
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, listProps: { className: 'test' } }));
|
|
207
|
+
expect(screen.getByRole('list')).toHaveClass('test');
|
|
208
|
+
});
|
|
209
|
+
it('Passes listProps to List when conversations is an object', () => {
|
|
210
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations }, listProps: { Today: { className: 'test' } } }));
|
|
211
|
+
expect(screen.getByRole('list')).toHaveClass('test');
|
|
212
|
+
});
|
|
213
|
+
it('Passes listItemProps to ListItem', () => {
|
|
214
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: [{ id: '1', text: 'ChatBot documentation', listItemProps: { className: 'test' } }] }));
|
|
215
|
+
expect(screen.getByRole('listitem')).toHaveClass('test');
|
|
216
|
+
});
|
|
217
|
+
it('should be able to spread search input props when searchInputProps is passed', () => {
|
|
218
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, handleTextInputChange: jest.fn(), searchInputProps: { value: 'I am a sample search' } }));
|
|
219
|
+
expect(screen.getByRole('dialog', { name: /Chat history I am a sample search/i })).toBeInTheDocument();
|
|
220
|
+
});
|
|
221
|
+
it('overrides nav title heading level when navTitleProps.headingLevel is passed', () => {
|
|
222
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations }, navTitleProps: { headingLevel: 'h1' } }));
|
|
223
|
+
expect(screen.queryByRole('heading', { name: /Chat history/i, level: 2 })).not.toBeInTheDocument();
|
|
224
|
+
expect(screen.getByRole('heading', { name: /Chat history/i, level: 1 })).toBeInTheDocument();
|
|
225
|
+
});
|
|
226
|
+
it('overrides nav title icon when navTitleIcon is passed in', () => {
|
|
227
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, navTitleIcon: _jsx(BellIcon, { "data-testid": "bell" }) }));
|
|
228
|
+
expect(screen.getByTestId('bell')).toBeInTheDocument();
|
|
229
|
+
});
|
|
179
230
|
});
|
|
@@ -14,7 +14,7 @@ import { forwardRef } from 'react';
|
|
|
14
14
|
import { Button, Icon, Tooltip } from '@patternfly/react-core';
|
|
15
15
|
import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
|
|
16
16
|
const ChatbotHeaderMenuBase = (_a) => {
|
|
17
|
-
var { className, onMenuToggle, tooltipProps, menuAriaLabel = '
|
|
17
|
+
var { className, onMenuToggle, tooltipProps, menuAriaLabel = 'Chat history menu', innerRef, tooltipContent = 'Chat history menu', isCompact } = _a, props = __rest(_a, ["className", "onMenuToggle", "tooltipProps", "menuAriaLabel", "innerRef", "tooltipContent", "isCompact"]);
|
|
18
18
|
return (_jsx("div", { className: `pf-chatbot__menu ${className}`, children: _jsx(Tooltip, Object.assign({ content: tooltipContent, position: "bottom",
|
|
19
19
|
// prevents VO announcements of both aria label and tooltip
|
|
20
20
|
aria: "none" }, tooltipProps, { children: _jsx(Button, Object.assign({ className: `pf-chatbot__button--toggle-menu ${isCompact ? 'pf-m-compact' : ''}`, variant: "plain", onClick: onMenuToggle, "aria-label": menuAriaLabel, ref: innerRef, icon: _jsx(Icon, { size: isCompact ? 'lg' : 'xl', isInline: true, children: _jsx(BarsIcon, {}) }), size: isCompact ? 'sm' : undefined }, props)) })) }));
|
|
@@ -10,7 +10,7 @@ describe('ChatbotHeaderMenu', () => {
|
|
|
10
10
|
it('should call onMenuToggle when ChatbotHeaderMenu button is clicked', () => {
|
|
11
11
|
const onMenuToggle = jest.fn();
|
|
12
12
|
render(_jsx(ChatbotHeaderMenu, { className: "custom-header-menu", onMenuToggle: onMenuToggle }));
|
|
13
|
-
fireEvent.click(screen.getByRole('button', { name: '
|
|
13
|
+
fireEvent.click(screen.getByRole('button', { name: 'Chat history menu' }));
|
|
14
14
|
expect(onMenuToggle).toHaveBeenCalled();
|
|
15
15
|
});
|
|
16
16
|
it('should handle isCompact', () => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ButtonProps, TooltipProps } from '@patternfly/react-core';
|
|
2
|
+
export interface ChatbotHeaderNewChatButtonProps extends ButtonProps {
|
|
3
|
+
/** Callback function for when button is clicked */
|
|
4
|
+
onClick: () => void;
|
|
5
|
+
/** Custom classname for the header component */
|
|
6
|
+
className?: string;
|
|
7
|
+
/** Props spread to the PF Tooltip component wrapping the display mode dropdown */
|
|
8
|
+
tooltipProps?: TooltipProps;
|
|
9
|
+
/** Aria label for menu */
|
|
10
|
+
menuAriaLabel?: string;
|
|
11
|
+
/** Ref applied to menu */
|
|
12
|
+
innerRef?: React.Ref<HTMLButtonElement>;
|
|
13
|
+
/** Content used in tooltip */
|
|
14
|
+
tooltipContent?: string;
|
|
15
|
+
/** Sets button to compact styling. */
|
|
16
|
+
isCompact?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare const ChatbotHeaderNewChatButton: import("react").ForwardRefExoticComponent<ChatbotHeaderNewChatButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,22 @@
|
|
|
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 } from "react/jsx-runtime";
|
|
13
|
+
import { forwardRef } from 'react';
|
|
14
|
+
import { Button, Icon, Tooltip } from '@patternfly/react-core';
|
|
15
|
+
import { PenToSquareIcon } from '@patternfly/react-icons/dist/esm/icons/pen-to-square-icon';
|
|
16
|
+
const ChatbotHeaderNewChatButtonBase = (_a) => {
|
|
17
|
+
var { className, onClick, tooltipProps, menuAriaLabel = 'New chat', innerRef, tooltipContent = 'New chat', isCompact } = _a, props = __rest(_a, ["className", "onClick", "tooltipProps", "menuAriaLabel", "innerRef", "tooltipContent", "isCompact"]);
|
|
18
|
+
return (_jsx("div", { className: `pf-chatbot__menu${className ? ` ${className}` : ''}`, children: _jsx(Tooltip, Object.assign({ content: tooltipContent, position: "bottom",
|
|
19
|
+
// prevents VO announcements of both aria label and tooltip
|
|
20
|
+
aria: "none" }, tooltipProps, { children: _jsx(Button, Object.assign({ className: `pf-chatbot__button--toggle-menu ${isCompact ? 'pf-m-compact' : ''}`, variant: "plain", onClick: onClick, "aria-label": menuAriaLabel, ref: innerRef, icon: _jsx(Icon, { size: isCompact ? 'lg' : 'xl', isInline: true, children: _jsx(PenToSquareIcon, {}) }), size: isCompact ? 'sm' : undefined }, props)) })) }));
|
|
21
|
+
};
|
|
22
|
+
export const ChatbotHeaderNewChatButton = forwardRef((props, ref) => (_jsx(ChatbotHeaderNewChatButtonBase, Object.assign({ innerRef: ref }, props))));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
+
import { ChatbotHeaderNewChatButton } from './ChatbotHeaderNewChatButton';
|
|
4
|
+
import '@testing-library/jest-dom';
|
|
5
|
+
describe('ChatbotHeaderNewChatButton', () => {
|
|
6
|
+
it('should render ChatbotHeaderNewChatButton', () => {
|
|
7
|
+
const { container } = render(_jsx(ChatbotHeaderNewChatButton, { className: "custom-header-new-chat-button", onClick: jest.fn() }));
|
|
8
|
+
expect(container.querySelector('.custom-header-new-chat-button')).toBeTruthy();
|
|
9
|
+
});
|
|
10
|
+
it('should call onClick handler when new chat button is pressed', () => {
|
|
11
|
+
const onClick = jest.fn();
|
|
12
|
+
render(_jsx(ChatbotHeaderNewChatButton, { className: "custom-header-new-chat-button", onClick: onClick }));
|
|
13
|
+
fireEvent.click(screen.getByRole('button', { name: 'New chat' }));
|
|
14
|
+
expect(onClick).toHaveBeenCalled();
|
|
15
|
+
});
|
|
16
|
+
it('should render button with isCompact', () => {
|
|
17
|
+
render(_jsx(ChatbotHeaderNewChatButton, { "data-testid": "new-chat-button", onClick: jest.fn(), isCompact: true }));
|
|
18
|
+
expect(screen.getByTestId('new-chat-button')).toHaveClass('pf-m-compact');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { DropEvent } from '@patternfly/react-core';
|
|
2
1
|
import type { FunctionComponent } from 'react';
|
|
3
2
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
4
|
-
import { Accept, FileError, FileRejection } from 'react-dropzone
|
|
3
|
+
import { Accept, DropEvent, FileError, FileRejection } from 'react-dropzone';
|
|
5
4
|
export interface FileDropZoneProps {
|
|
6
5
|
/** Content displayed when the drop zone is not currently in use */
|
|
7
6
|
children?: React.ReactNode;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react';
|
|
3
|
+
import { Options } from 'react-markdown';
|
|
3
4
|
import { AlertProps, AvatarProps, ButtonProps, ExpandableSectionProps, ExpandableSectionToggleProps, FormProps, LabelGroupProps } from '@patternfly/react-core';
|
|
4
5
|
import { ActionProps } from '../ResponseActions/ResponseActions';
|
|
5
6
|
import { SourcesCardProps } from '../SourcesCard';
|
|
@@ -8,7 +9,7 @@ import QuickResponse from './QuickResponse/QuickResponse';
|
|
|
8
9
|
import { UserFeedbackProps } from './UserFeedback/UserFeedback';
|
|
9
10
|
import { UserFeedbackCompleteProps } from './UserFeedback/UserFeedbackComplete';
|
|
10
11
|
import { TableProps } from '@patternfly/react-table';
|
|
11
|
-
import { PluggableList } from '
|
|
12
|
+
import { PluggableList } from 'unified';
|
|
12
13
|
export interface MessageAttachment {
|
|
13
14
|
/** Name of file attached to the message */
|
|
14
15
|
name: string;
|
|
@@ -54,7 +55,7 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
54
55
|
isLoading?: boolean;
|
|
55
56
|
/** Array of attachments attached to a message */
|
|
56
57
|
attachments?: MessageAttachment[];
|
|
57
|
-
/** Props for message actions, such as feedback (positive or negative), copy button, share, and listen */
|
|
58
|
+
/** Props for message actions, such as feedback (positive or negative), copy button, edit message, share, and listen */
|
|
58
59
|
actions?: {
|
|
59
60
|
[key: string]: ActionProps;
|
|
60
61
|
};
|
|
@@ -134,10 +135,16 @@ export interface MessageProps extends Omit<HTMLProps<HTMLDivElement>, 'role'> {
|
|
|
134
135
|
onEditUpdate?: (event: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
135
136
|
/** Callback functionf or when edit cancel update button is clicked */
|
|
136
137
|
onEditCancel?: (event: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
138
|
+
/** Ref applied to editable message input */
|
|
139
|
+
inputRef?: Ref<HTMLTextAreaElement>;
|
|
137
140
|
/** Props for edit form */
|
|
138
141
|
editFormProps?: FormProps;
|
|
139
142
|
/** Sets message to compact styling. */
|
|
140
143
|
isCompact?: boolean;
|
|
144
|
+
/** Disables markdown parsing for message, allowing only text input */
|
|
145
|
+
isMarkdownDisabled?: boolean;
|
|
146
|
+
/** Allows passing additional props down to markdown parser react-markdown, such as allowedElements and disallowedElements. See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#options for options */
|
|
147
|
+
reactMarkdownProps?: Options;
|
|
141
148
|
}
|
|
142
149
|
export declare const MessageBase: FunctionComponent<MessageProps>;
|
|
143
150
|
declare const Message: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -45,7 +45,7 @@ import ErrorMessage from './ErrorMessage/ErrorMessage';
|
|
|
45
45
|
import MessageInput from './MessageInput';
|
|
46
46
|
import { rehypeMoveImagesOutOfParagraphs } from './Plugins/rehypeMoveImagesOutOfParagraphs';
|
|
47
47
|
export const MessageBase = (_a) => {
|
|
48
|
-
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, editFormProps, isCompact } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "editFormProps", "isCompact"]);
|
|
48
|
+
var { role, content, extraContent, name, avatar, timestamp, isLoading, actions, sources, botWord = 'AI', loadingWord = 'Loading message', codeBlockProps, quickResponses, quickResponseContainerProps = { numLabels: 5 }, attachments, hasRoundAvatar = true, avatarProps, quickStarts, userFeedbackForm, userFeedbackComplete, isLiveRegion = true, innerRef, tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], linkProps, error, isEditable, editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, inputRef, editFormProps, isCompact, isMarkdownDisabled, reactMarkdownProps } = _a, props = __rest(_a, ["role", "content", "extraContent", "name", "avatar", "timestamp", "isLoading", "actions", "sources", "botWord", "loadingWord", "codeBlockProps", "quickResponses", "quickResponseContainerProps", "attachments", "hasRoundAvatar", "avatarProps", "quickStarts", "userFeedbackForm", "userFeedbackComplete", "isLiveRegion", "innerRef", "tableProps", "openLinkInNewTab", "additionalRehypePlugins", "linkProps", "error", "isEditable", "editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "inputRef", "editFormProps", "isCompact", "isMarkdownDisabled", "reactMarkdownProps"]);
|
|
49
49
|
const [messageText, setMessageText] = useState(content);
|
|
50
50
|
useEffect(() => {
|
|
51
51
|
setMessageText(content);
|
|
@@ -67,48 +67,54 @@ export const MessageBase = (_a) => {
|
|
|
67
67
|
// Keep timestamps consistent between Timestamp component and aria-label
|
|
68
68
|
const date = new Date();
|
|
69
69
|
const dateString = timestamp !== null && timestamp !== void 0 ? timestamp : `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
|
|
70
|
+
const handleMarkdown = () => {
|
|
71
|
+
if (isMarkdownDisabled) {
|
|
72
|
+
return (_jsx(TextMessage, Object.assign({ component: ContentVariants.p }, props, { children: messageText })));
|
|
73
|
+
}
|
|
74
|
+
return (_jsx(Markdown, Object.assign({ components: {
|
|
75
|
+
p: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, props)),
|
|
76
|
+
code: (_a) => {
|
|
77
|
+
var { children } = _a, props = __rest(_a, ["children"]);
|
|
78
|
+
return (_jsx(CodeBlockMessage, Object.assign({}, props, codeBlockProps, { children: children })));
|
|
79
|
+
},
|
|
80
|
+
h1: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h1 }, props)),
|
|
81
|
+
h2: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h2 }, props)),
|
|
82
|
+
h3: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h3 }, props)),
|
|
83
|
+
h4: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h4 }, props)),
|
|
84
|
+
h5: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h5 }, props)),
|
|
85
|
+
h6: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h6 }, props)),
|
|
86
|
+
blockquote: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.blockquote }, props)),
|
|
87
|
+
ul: (props) => _jsx(UnorderedListMessage, Object.assign({}, props)),
|
|
88
|
+
ol: (props) => _jsx(OrderedListMessage, Object.assign({}, props)),
|
|
89
|
+
li: (props) => _jsx(ListItemMessage, Object.assign({}, props)),
|
|
90
|
+
table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps)),
|
|
91
|
+
tbody: (props) => _jsx(TbodyMessage, Object.assign({}, props)),
|
|
92
|
+
thead: (props) => _jsx(TheadMessage, Object.assign({}, props)),
|
|
93
|
+
tr: (props) => _jsx(TrMessage, Object.assign({}, props)),
|
|
94
|
+
td: (props) => {
|
|
95
|
+
// Conflicts with Td type
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
97
|
+
const { width } = props, rest = __rest(props, ["width"]);
|
|
98
|
+
return _jsx(TdMessage, Object.assign({}, rest));
|
|
99
|
+
},
|
|
100
|
+
th: (props) => _jsx(ThMessage, Object.assign({}, props)),
|
|
101
|
+
img: (props) => _jsx(ImageMessage, Object.assign({}, props)),
|
|
102
|
+
a: (props) => (_jsx(LinkMessage, Object.assign({ href: props.href, rel: props.rel, target: props.target }, linkProps, { children: props.children })))
|
|
103
|
+
}, remarkPlugins: [remarkGfm], rehypePlugins: rehypePlugins }, reactMarkdownProps, { children: messageText })));
|
|
104
|
+
};
|
|
70
105
|
const renderMessage = () => {
|
|
71
106
|
if (isLoading) {
|
|
72
107
|
return _jsx(MessageLoading, { loadingWord: loadingWord });
|
|
73
108
|
}
|
|
74
109
|
if (isEditable) {
|
|
75
|
-
return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), _jsx(MessageInput, Object.assign({ content:
|
|
110
|
+
return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), _jsx(MessageInput, Object.assign({ content: messageText, editPlaceholder: editPlaceholder, updateWord: updateWord, cancelWord: cancelWord, onEditUpdate: (event, value) => {
|
|
76
111
|
onEditUpdate && onEditUpdate(event);
|
|
77
112
|
setMessageText(value);
|
|
78
|
-
}, onEditCancel: onEditCancel }, editFormProps))] }));
|
|
113
|
+
}, onEditCancel: onEditCancel, inputRef: inputRef }, editFormProps))] }));
|
|
79
114
|
}
|
|
80
|
-
return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), error ?
|
|
81
|
-
p: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.p }, props)),
|
|
82
|
-
code: (_a) => {
|
|
83
|
-
var { children } = _a, props = __rest(_a, ["children"]);
|
|
84
|
-
return (_jsx(CodeBlockMessage, Object.assign({}, props, codeBlockProps, { children: children })));
|
|
85
|
-
},
|
|
86
|
-
h1: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h1 }, props)),
|
|
87
|
-
h2: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h2 }, props)),
|
|
88
|
-
h3: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h3 }, props)),
|
|
89
|
-
h4: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h4 }, props)),
|
|
90
|
-
h5: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h5 }, props)),
|
|
91
|
-
h6: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.h6 }, props)),
|
|
92
|
-
blockquote: (props) => _jsx(TextMessage, Object.assign({ component: ContentVariants.blockquote }, props)),
|
|
93
|
-
ul: (props) => _jsx(UnorderedListMessage, Object.assign({}, props)),
|
|
94
|
-
ol: (props) => _jsx(OrderedListMessage, Object.assign({}, props)),
|
|
95
|
-
li: (props) => _jsx(ListItemMessage, Object.assign({}, props)),
|
|
96
|
-
table: (props) => _jsx(TableMessage, Object.assign({}, props, tableProps)),
|
|
97
|
-
tbody: (props) => _jsx(TbodyMessage, Object.assign({}, props)),
|
|
98
|
-
thead: (props) => _jsx(TheadMessage, Object.assign({}, props)),
|
|
99
|
-
tr: (props) => _jsx(TrMessage, Object.assign({}, props)),
|
|
100
|
-
td: (props) => {
|
|
101
|
-
// Conflicts with Td type
|
|
102
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
103
|
-
const { width } = props, rest = __rest(props, ["width"]);
|
|
104
|
-
return _jsx(TdMessage, Object.assign({}, rest));
|
|
105
|
-
},
|
|
106
|
-
th: (props) => _jsx(ThMessage, Object.assign({}, props)),
|
|
107
|
-
img: (props) => _jsx(ImageMessage, Object.assign({}, props)),
|
|
108
|
-
a: (props) => (_jsx(LinkMessage, Object.assign({ href: props.href, rel: props.rel, target: props.target }, linkProps, { children: props.children })))
|
|
109
|
-
}, remarkPlugins: [remarkGfm], rehypePlugins: rehypePlugins, children: messageText }))] }));
|
|
115
|
+
return (_jsxs(_Fragment, { children: [beforeMainContent && _jsx(_Fragment, { children: beforeMainContent }), error ? _jsx(ErrorMessage, Object.assign({}, error)) : handleMarkdown()] }));
|
|
110
116
|
};
|
|
111
|
-
return (_jsxs("section", Object.assign({ "aria-label": `Message from ${role} - ${dateString}`, className: `pf-chatbot__message pf-chatbot__message--${role}`, "aria-live": isLiveRegion ? 'polite' : undefined, "aria-atomic": isLiveRegion ? false : undefined, ref: innerRef }, props, { children: [_jsx(Avatar, Object.assign({ className: `pf-chatbot__message-avatar ${hasRoundAvatar ? 'pf-chatbot__message-avatar--round' : ''} ${avatarClassName ? avatarClassName : ''}`, src: avatar, alt: "" }, avatarProps)), _jsxs("div", { className: "pf-chatbot__message-contents", children: [_jsxs("div", { className: "pf-chatbot__message-meta", children: [name && (_jsx("span", { className: "pf-chatbot__message-name", children: _jsx(Truncate, { content: name }) })), role === 'bot' && (_jsx(Label, { variant: "outline", isCompact: true, children: botWord })), _jsx(Timestamp, { date: date, children: timestamp })] }), _jsxs("div", { className: "pf-chatbot__message-response", children: [_jsxs("div", { className: "pf-chatbot__message-and-actions", children: [renderMessage(), afterMainContent && _jsx(_Fragment, { children: afterMainContent }), !isLoading && sources && _jsx(SourcesCard, Object.assign({}, sources, { isCompact: isCompact })), quickStarts && quickStarts.quickStart && (_jsx(QuickStartTile, { quickStart: quickStarts.quickStart, onSelectQuickStart: quickStarts.onSelectQuickStart, minuteWord: quickStarts.minuteWord, minuteWordPlural: quickStarts.minuteWordPlural, prerequisiteWord: quickStarts.prerequisiteWord, prerequisiteWordPlural: quickStarts.prerequisiteWordPlural, quickStartButtonAriaLabel: quickStarts.quickStartButtonAriaLabel, isCompact: isCompact })), !isLoading && actions && _jsx(ResponseActions, { actions: actions }), userFeedbackForm && _jsx(UserFeedback, Object.assign({}, userFeedbackForm, { timestamp: dateString, isCompact: isCompact })), userFeedbackComplete && (_jsx(UserFeedbackComplete, Object.assign({}, userFeedbackComplete, { timestamp: dateString, isCompact: isCompact }))), !isLoading && quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, isCompact: isCompact }))] }), attachments && (_jsx("div", { className: "pf-chatbot__message-attachments-container", children: attachments.map((attachment) => {
|
|
117
|
+
return (_jsxs("section", Object.assign({ "aria-label": `Message from ${role} - ${dateString}`, className: `pf-chatbot__message pf-chatbot__message--${role}`, "aria-live": isLiveRegion ? 'polite' : undefined, "aria-atomic": isLiveRegion ? false : undefined, ref: innerRef }, props, { children: [_jsx(Avatar, Object.assign({ className: `pf-chatbot__message-avatar ${hasRoundAvatar ? 'pf-chatbot__message-avatar--round' : ''} ${avatarClassName ? avatarClassName : ''}`, src: avatar, alt: "" }, avatarProps)), _jsxs("div", { className: "pf-chatbot__message-contents", children: [_jsxs("div", { className: "pf-chatbot__message-meta", children: [name && (_jsx("span", { className: "pf-chatbot__message-name", children: _jsx(Truncate, { content: name }) })), role === 'bot' && (_jsx(Label, { variant: "outline", isCompact: true, children: botWord })), _jsx(Timestamp, { date: date, children: timestamp })] }), _jsxs("div", { className: "pf-chatbot__message-response", children: [_jsxs("div", { className: "pf-chatbot__message-and-actions", children: [renderMessage(), afterMainContent && _jsx(_Fragment, { children: afterMainContent }), !isLoading && sources && _jsx(SourcesCard, Object.assign({}, sources, { isCompact: isCompact })), quickStarts && quickStarts.quickStart && (_jsx(QuickStartTile, { quickStart: quickStarts.quickStart, onSelectQuickStart: quickStarts.onSelectQuickStart, minuteWord: quickStarts.minuteWord, minuteWordPlural: quickStarts.minuteWordPlural, prerequisiteWord: quickStarts.prerequisiteWord, prerequisiteWordPlural: quickStarts.prerequisiteWordPlural, quickStartButtonAriaLabel: quickStarts.quickStartButtonAriaLabel, isCompact: isCompact })), !isLoading && !isEditable && actions && _jsx(ResponseActions, { actions: actions }), userFeedbackForm && _jsx(UserFeedback, Object.assign({}, userFeedbackForm, { timestamp: dateString, isCompact: isCompact })), userFeedbackComplete && (_jsx(UserFeedbackComplete, Object.assign({}, userFeedbackComplete, { timestamp: dateString, isCompact: isCompact }))), !isLoading && quickResponses && (_jsx(QuickResponse, { quickResponses: quickResponses, quickResponseContainerProps: quickResponseContainerProps, isCompact: isCompact }))] }), attachments && (_jsx("div", { className: "pf-chatbot__message-attachments-container", children: attachments.map((attachment) => {
|
|
112
118
|
var _a;
|
|
113
119
|
return (_jsx("div", { className: "pf-chatbot__message-attachment", children: _jsx(FileDetailsLabel, { fileName: attachment.name, fileId: attachment.id, onClose: attachment.onClose, onClick: attachment.onClick, isLoading: attachment.isLoading, closeButtonAriaLabel: attachment.closeButtonAriaLabel, languageTestId: attachment.languageTestId, spinnerTestId: attachment.spinnerTestId }) }, (_a = attachment.id) !== null && _a !== void 0 ? _a : attachment.name));
|
|
114
120
|
}) })), !isLoading && endContent && _jsx(_Fragment, { children: endContent })] })] })] })));
|
|
@@ -21,6 +21,7 @@ const ALL_ACTIONS = [
|
|
|
21
21
|
{ label: /Good response/i },
|
|
22
22
|
{ label: /Bad response/i },
|
|
23
23
|
{ label: /Copy/i },
|
|
24
|
+
{ label: /Edit/i },
|
|
24
25
|
{ label: /Share/i },
|
|
25
26
|
{ label: /Listen/i }
|
|
26
27
|
];
|
|
@@ -341,6 +342,8 @@ describe('Message', () => {
|
|
|
341
342
|
// eslint-disable-next-line no-console
|
|
342
343
|
copy: { onClick: () => console.log('Copy') },
|
|
343
344
|
// eslint-disable-next-line no-console
|
|
345
|
+
edit: { onClick: () => console.log('Edit') },
|
|
346
|
+
// eslint-disable-next-line no-console
|
|
344
347
|
share: { onClick: () => console.log('Share') },
|
|
345
348
|
// eslint-disable-next-line no-console
|
|
346
349
|
download: { onClick: () => console.log('Download') },
|
|
@@ -360,6 +363,8 @@ describe('Message', () => {
|
|
|
360
363
|
// eslint-disable-next-line no-console
|
|
361
364
|
copy: { onClick: () => console.log('Copy') },
|
|
362
365
|
// eslint-disable-next-line no-console
|
|
366
|
+
edit: { onClick: () => console.log('Edit') },
|
|
367
|
+
// eslint-disable-next-line no-console
|
|
363
368
|
share: { onClick: () => console.log('Share') },
|
|
364
369
|
// eslint-disable-next-line no-console
|
|
365
370
|
download: { onClick: () => console.log('Download') },
|
|
@@ -371,6 +376,27 @@ describe('Message', () => {
|
|
|
371
376
|
expect(screen.queryByRole('button', { name: label })).toBeFalsy();
|
|
372
377
|
});
|
|
373
378
|
}));
|
|
379
|
+
it('should not show actions if isEditable is true', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
380
|
+
render(_jsx(Message, { avatar: "./img", role: "bot", name: "Bot", content: "Hi", isEditable: true, actions: {
|
|
381
|
+
// eslint-disable-next-line no-console
|
|
382
|
+
positive: { onClick: () => console.log('Good response') },
|
|
383
|
+
// eslint-disable-next-line no-console
|
|
384
|
+
negative: { onClick: () => console.log('Bad response') },
|
|
385
|
+
// eslint-disable-next-line no-console
|
|
386
|
+
copy: { onClick: () => console.log('Copy') },
|
|
387
|
+
// eslint-disable-next-line no-console
|
|
388
|
+
edit: { onClick: () => console.log('Edit') },
|
|
389
|
+
// eslint-disable-next-line no-console
|
|
390
|
+
share: { onClick: () => console.log('Share') },
|
|
391
|
+
// eslint-disable-next-line no-console
|
|
392
|
+
download: { onClick: () => console.log('Download') },
|
|
393
|
+
// eslint-disable-next-line no-console
|
|
394
|
+
listen: { onClick: () => console.log('Listen') }
|
|
395
|
+
} }));
|
|
396
|
+
ALL_ACTIONS.forEach(({ label }) => {
|
|
397
|
+
expect(screen.queryByRole('button', { name: label })).toBeFalsy();
|
|
398
|
+
});
|
|
399
|
+
}));
|
|
374
400
|
it('should render unordered lists correctly', () => {
|
|
375
401
|
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: UNORDERED_LIST }));
|
|
376
402
|
expect(screen.getByText('Here is an unordered list:')).toBeTruthy();
|
|
@@ -708,4 +734,15 @@ describe('Message', () => {
|
|
|
708
734
|
const form = container.querySelector('form');
|
|
709
735
|
expect(form).toHaveClass('test');
|
|
710
736
|
});
|
|
737
|
+
it('should be able to disable markdown parsing', () => {
|
|
738
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, isMarkdownDisabled: true }));
|
|
739
|
+
// this is looking for markdown syntax that is ordinarily stripped
|
|
740
|
+
expect(screen.getByText(/~~~yaml/i)).toBeTruthy();
|
|
741
|
+
});
|
|
742
|
+
it('should be able to pass props to react-markdown, such as disabling tags', () => {
|
|
743
|
+
render(_jsx(Message, { avatar: "./img", role: "user", name: "User", content: CODE_MESSAGE, reactMarkdownProps: { disallowedElements: ['code'] } }));
|
|
744
|
+
expect(screen.getByText('Here is some YAML code:')).toBeTruthy();
|
|
745
|
+
// code block isn't rendering
|
|
746
|
+
expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy();
|
|
747
|
+
});
|
|
711
748
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FunctionComponent } from 'react';
|
|
1
|
+
import type { FunctionComponent, Ref } from 'react';
|
|
2
2
|
import { FormProps } from '@patternfly/react-core';
|
|
3
3
|
export interface MessageInputProps extends FormProps {
|
|
4
4
|
/** Placeholder for edit input */
|
|
@@ -11,6 +11,8 @@ export interface MessageInputProps extends FormProps {
|
|
|
11
11
|
onEditUpdate?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, value: string) => void;
|
|
12
12
|
/** Callback functionf or when edit cancel update button is clicked */
|
|
13
13
|
onEditCancel?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
|
14
|
+
/** Ref applied to editable message input */
|
|
15
|
+
inputRef?: Ref<HTMLTextAreaElement>;
|
|
14
16
|
/** Message text */
|
|
15
17
|
content?: string;
|
|
16
18
|
}
|
|
@@ -13,11 +13,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
import { useState } from 'react';
|
|
14
14
|
import { ActionGroup, Button, Form, TextArea } from '@patternfly/react-core';
|
|
15
15
|
const MessageInput = (_a) => {
|
|
16
|
-
var { editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, content } = _a, props = __rest(_a, ["editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "content"]);
|
|
16
|
+
var { editPlaceholder = 'Edit prompt message...', updateWord = 'Update', cancelWord = 'Cancel', onEditUpdate, onEditCancel, inputRef, content } = _a, props = __rest(_a, ["editPlaceholder", "updateWord", "cancelWord", "onEditUpdate", "onEditCancel", "inputRef", "content"]);
|
|
17
17
|
const [messageText, setMessageText] = useState(content !== null && content !== void 0 ? content : '');
|
|
18
18
|
const onChange = (_event, value) => {
|
|
19
19
|
setMessageText(value);
|
|
20
20
|
};
|
|
21
|
-
return (_jsxs(Form, Object.assign({}, props, { children: [_jsx(TextArea, { placeholder: editPlaceholder, value: messageText, onChange: onChange, "aria-label": editPlaceholder, autoResize: true }), _jsxs(ActionGroup, { className: "pf-chatbot__message-edit-buttons", children: [_jsx(Button, { variant: "primary", onClick: (event) => onEditUpdate && onEditUpdate(event, messageText), children: updateWord }), _jsx(Button, { variant: "secondary", onClick: onEditCancel, children: cancelWord })] })] })));
|
|
21
|
+
return (_jsxs(Form, Object.assign({}, props, { children: [_jsx(TextArea, { placeholder: editPlaceholder, value: messageText, onChange: onChange, "aria-label": editPlaceholder, autoResize: true, ref: inputRef }), _jsxs(ActionGroup, { className: "pf-chatbot__message-edit-buttons", children: [_jsx(Button, { variant: "primary", onClick: (event) => onEditUpdate && onEditUpdate(event, messageText), children: updateWord }), _jsx(Button, { variant: "secondary", onClick: onEditCancel, children: cancelWord })] })] })));
|
|
22
22
|
};
|
|
23
23
|
export default MessageInput;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ButtonProps,
|
|
2
|
-
import { Accept, DropzoneOptions, FileError, FileRejection } from 'react-dropzone';
|
|
1
|
+
import { ButtonProps, TooltipProps } from '@patternfly/react-core';
|
|
2
|
+
import { Accept, DropEvent, DropzoneOptions, FileError, FileRejection } from 'react-dropzone';
|
|
3
3
|
export interface AttachButtonProps extends ButtonProps {
|
|
4
4
|
/** Callback for when button is clicked */
|
|
5
5
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
|
2
|
-
import { Accept, DropzoneOptions, FileError, FileRejection } from 'react-dropzone
|
|
3
|
-
import { ButtonProps,
|
|
2
|
+
import { Accept, DropEvent, DropzoneOptions, FileError, FileRejection } from 'react-dropzone';
|
|
3
|
+
import { ButtonProps, TextAreaProps, TooltipProps } from '@patternfly/react-core';
|
|
4
4
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
5
5
|
export interface MessageBarWithAttachMenuProps {
|
|
6
6
|
/** Flag to enable whether attach menu is open */
|
|
@@ -26,6 +26,7 @@ export const MessageBarBase = (_a) => {
|
|
|
26
26
|
const [message, setMessage] = useState(value !== null && value !== void 0 ? value : '');
|
|
27
27
|
const [isListeningMessage, setIsListeningMessage] = useState(false);
|
|
28
28
|
const [hasSentMessage, setHasSentMessage] = useState(false);
|
|
29
|
+
const [isComposing, setIsComposing] = useState(false);
|
|
29
30
|
const inputRef = useRef(null);
|
|
30
31
|
const textareaRef = (_b = innerRef) !== null && _b !== void 0 ? _b : inputRef;
|
|
31
32
|
const attachButtonRef = useRef(null);
|
|
@@ -151,18 +152,32 @@ export const MessageBarBase = (_a) => {
|
|
|
151
152
|
setMessage('');
|
|
152
153
|
}, [onSendMessage]);
|
|
153
154
|
const handleKeyDown = useCallback((event) => {
|
|
154
|
-
|
|
155
|
+
// Japanese and other languages may use IME for character input.
|
|
156
|
+
// In these cases, enter is used to select the final input, so we need to check for composition end instead.
|
|
157
|
+
// See more info at https://www.stum.de/2016/06/24/handling-ime-events-in-javascript/
|
|
158
|
+
// Chrome, Edge, and Firefox seem to work well with just the compose event.
|
|
159
|
+
// Safari is a little bit special. We need to handle 229 as well in this case.
|
|
160
|
+
const nativeEvent = event.nativeEvent;
|
|
161
|
+
const isCompositionKey = nativeEvent.which === 229;
|
|
162
|
+
const isCurrentlyComposing = isComposing || isCompositionKey;
|
|
163
|
+
if (event.key === 'Enter' && !isCurrentlyComposing && !event.shiftKey) {
|
|
155
164
|
event.preventDefault();
|
|
156
165
|
if (!isSendButtonDisabled && !hasStopButton) {
|
|
157
166
|
handleSend(message);
|
|
158
167
|
}
|
|
159
168
|
}
|
|
160
|
-
if (event.key === 'Enter' && event.shiftKey) {
|
|
169
|
+
if (event.key === 'Enter' && !isCurrentlyComposing && event.shiftKey) {
|
|
161
170
|
if (textareaRef.current) {
|
|
162
171
|
handleNewLine(textareaRef.current);
|
|
163
172
|
}
|
|
164
173
|
}
|
|
165
|
-
}, [isSendButtonDisabled, hasStopButton, handleSend, message]);
|
|
174
|
+
}, [isSendButtonDisabled, hasStopButton, handleSend, message, isComposing]);
|
|
175
|
+
const handleCompositionStart = useCallback(() => {
|
|
176
|
+
setIsComposing(true);
|
|
177
|
+
}, []);
|
|
178
|
+
const handleCompositionEnd = useCallback(() => {
|
|
179
|
+
setIsComposing(false);
|
|
180
|
+
}, []);
|
|
166
181
|
const handleAttachMenuToggle = () => {
|
|
167
182
|
(attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.setIsAttachMenuOpen) && (attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.setIsAttachMenuOpen(!(attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.isAttachMenuOpen)));
|
|
168
183
|
attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.onAttachMenuToggleClick();
|
|
@@ -178,7 +193,7 @@ export const MessageBarBase = (_a) => {
|
|
|
178
193
|
}
|
|
179
194
|
return (_jsxs(_Fragment, { children: [attachMenuProps && (_jsx(AttachButton, Object.assign({ ref: attachButtonRef, onClick: handleAttachMenuToggle, isDisabled: isListeningMessage, tooltipContent: (_d = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _d === void 0 ? void 0 : _d.tooltipContent, isCompact: isCompact, tooltipProps: (_e = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _e === void 0 ? void 0 : _e.tooltipProps, allowedFileTypes: allowedFileTypes, minSize: minSize, maxSize: maxSize, maxFiles: maxFiles, isAttachmentDisabled: isAttachmentDisabled, onAttach: onAttach, onAttachRejected: onAttachRejected, validator: validator, dropzoneProps: dropzoneProps }, (_f = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _f === void 0 ? void 0 : _f.props))), !attachMenuProps && hasAttachButton && (_jsx(AttachButton, Object.assign({ onAttachAccepted: handleAttach, isDisabled: isListeningMessage, tooltipContent: (_g = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _g === void 0 ? void 0 : _g.tooltipContent, inputTestId: (_h = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _h === void 0 ? void 0 : _h.inputTestId, isCompact: isCompact, tooltipProps: (_j = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _j === void 0 ? void 0 : _j.tooltipProps, allowedFileTypes: allowedFileTypes, minSize: minSize, maxSize: maxSize, maxFiles: maxFiles, isAttachmentDisabled: isAttachmentDisabled, onAttach: onAttach, onAttachRejected: onAttachRejected, validator: validator, dropzoneProps: dropzoneProps }, (_k = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.attach) === null || _k === void 0 ? void 0 : _k.props))), hasMicrophoneButton && (_jsx(MicrophoneButton, Object.assign({ isListening: isListeningMessage, onIsListeningChange: setIsListeningMessage, onSpeechRecognition: handleSpeechRecognition, tooltipContent: (_l = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _l === void 0 ? void 0 : _l.tooltipContent, language: (_m = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _m === void 0 ? void 0 : _m.language, isCompact: isCompact, tooltipProps: (_o = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _o === void 0 ? void 0 : _o.tooltipProps }, (_p = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.microphone) === null || _p === void 0 ? void 0 : _p.props))), (alwayShowSendButton || message) && (_jsx(SendButton, Object.assign({ value: message, onClick: () => handleSend(message), isDisabled: isSendButtonDisabled, tooltipContent: (_q = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _q === void 0 ? void 0 : _q.tooltipContent, isCompact: isCompact, tooltipProps: (_r = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _r === void 0 ? void 0 : _r.tooltipProps }, (_s = buttonProps === null || buttonProps === void 0 ? void 0 : buttonProps.send) === null || _s === void 0 ? void 0 : _s.props)))] }));
|
|
180
195
|
};
|
|
181
|
-
const messageBarContents = (_jsxs(_Fragment, { children: [_jsx("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}`, children: _jsx(TextArea, Object.assign({ className: "pf-chatbot__message-textarea", value: message, onChange: handleChange, "aria-label": isListeningMessage ? listeningText : placeholder, placeholder: isListeningMessage ? listeningText : placeholder, ref: textareaRef, onKeyDown: handleKeyDown }, props)) }), _jsx("div", { className: "pf-chatbot__message-bar-actions", children: renderButtons() })] }));
|
|
196
|
+
const messageBarContents = (_jsxs(_Fragment, { children: [_jsx("div", { className: `pf-chatbot__message-bar-input ${isCompact ? 'pf-m-compact' : ''}`, children: _jsx(TextArea, Object.assign({ className: "pf-chatbot__message-textarea", value: message, onChange: handleChange, "aria-label": isListeningMessage ? listeningText : placeholder, placeholder: isListeningMessage ? listeningText : placeholder, ref: textareaRef, onKeyDown: handleKeyDown, onCompositionStart: handleCompositionStart, onCompositionEnd: handleCompositionEnd }, props)) }), _jsx("div", { className: "pf-chatbot__message-bar-actions", children: renderButtons() })] }));
|
|
182
197
|
if (attachMenuProps) {
|
|
183
198
|
return (_jsx(AttachMenu, Object.assign({ toggle: (toggleRef) => (_jsx("div", { ref: toggleRef, className: `pf-chatbot__message-bar ${className !== null && className !== void 0 ? className : ''}`, children: messageBarContents })), filteredItems: attachMenuProps === null || attachMenuProps === void 0 ? void 0 : attachMenuProps.attachMenuItems }, (attachMenuProps && { isOpen: attachMenuProps.isAttachMenuOpen }), { onOpenChange: (isAttachMenuOpen) => {
|
|
184
199
|
var _a;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
|
2
|
+
import { TooltipProps, ButtonProps } from '@patternfly/react-core';
|
|
2
3
|
export interface JumpButtonProps {
|
|
3
4
|
/** Position of the Jump Button(top/bottom) */
|
|
4
5
|
position: 'top' | 'bottom';
|
|
@@ -6,6 +7,10 @@ export interface JumpButtonProps {
|
|
|
6
7
|
onClick: () => void;
|
|
7
8
|
/** Flag to change the visibilty of the button */
|
|
8
9
|
isHidden?: boolean;
|
|
10
|
+
/** Additional props passed to jump buttons */
|
|
11
|
+
jumpButtonProps?: ButtonProps;
|
|
12
|
+
/** Additional props passed to tooltip */
|
|
13
|
+
jumpButtonTooltipProps?: TooltipProps;
|
|
9
14
|
}
|
|
10
15
|
declare const JumpButton: FunctionComponent<JumpButtonProps>;
|
|
11
16
|
export default JumpButton;
|
|
@@ -3,5 +3,5 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { Button, Tooltip, Icon } from '@patternfly/react-core';
|
|
4
4
|
import { ArrowUpIcon } from '@patternfly/react-icons/dist/esm/icons/arrow-up-icon';
|
|
5
5
|
import { ArrowDownIcon } from '@patternfly/react-icons/dist/esm/icons/arrow-down-icon';
|
|
6
|
-
const JumpButton = ({ position, isHidden, onClick }) => isHidden ? null : (_jsx(Tooltip, { id: `pf-chatbot__tooltip--jump-${position}`, content: `Back to ${position}`, position: "top", children: _jsx(Button, { variant: "plain", className: `pf-chatbot__jump pf-chatbot__jump--${position}`, "aria-label": `
|
|
6
|
+
const JumpButton = ({ position, isHidden, onClick, jumpButtonProps, jumpButtonTooltipProps }) => isHidden ? null : (_jsx(Tooltip, Object.assign({ id: `pf-chatbot__tooltip--jump-${position}`, content: `Back to ${position}`, position: "top" }, jumpButtonTooltipProps, { children: _jsx(Button, Object.assign({ variant: "plain", className: `pf-chatbot__jump pf-chatbot__jump--${position}`, "aria-label": `Back to ${position}`, onClick: onClick }, jumpButtonProps, { children: _jsx(Icon, { iconSize: "lg", isInline: true, children: position === 'top' ? _jsx(ArrowUpIcon, {}) : _jsx(ArrowDownIcon, {}) }) })) })));
|
|
7
7
|
export default JumpButton;
|
|
@@ -15,20 +15,20 @@ import userEvent from '@testing-library/user-event';
|
|
|
15
15
|
describe('JumpButton', () => {
|
|
16
16
|
it('should render top button correctly', () => {
|
|
17
17
|
render(_jsx(JumpButton, { position: "top", onClick: jest.fn() }));
|
|
18
|
-
expect(screen.getByRole('button', { name: /
|
|
18
|
+
expect(screen.getByRole('button', { name: /Back to top/i })).toBeTruthy();
|
|
19
19
|
});
|
|
20
20
|
it('should render bottom button correctly', () => {
|
|
21
21
|
render(_jsx(JumpButton, { position: "bottom", onClick: jest.fn() }));
|
|
22
|
-
expect(screen.getByRole('button', { name: /
|
|
22
|
+
expect(screen.getByRole('button', { name: /Back to bottom/i })).toBeTruthy();
|
|
23
23
|
});
|
|
24
24
|
it('should call onClick appropriately', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
25
|
const spy = jest.fn();
|
|
26
26
|
render(_jsx(JumpButton, { position: "bottom", onClick: spy }));
|
|
27
|
-
yield userEvent.click(screen.getByRole('button', { name: /
|
|
27
|
+
yield userEvent.click(screen.getByRole('button', { name: /Back to bottom/i }));
|
|
28
28
|
expect(spy).toHaveBeenCalledTimes(1);
|
|
29
29
|
}));
|
|
30
30
|
it('should be hidden if isHidden prop is used', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
31
31
|
render(_jsx(JumpButton, { position: "bottom", onClick: jest.fn(), isHidden: true }));
|
|
32
|
-
expect(screen.queryByRole('button', { name: /
|
|
32
|
+
expect(screen.queryByRole('button', { name: /Back to bottom/i })).toBeFalsy();
|
|
33
33
|
}));
|
|
34
34
|
});
|