@patternfly/chatbot 6.4.0-prerelease.14 → 6.4.0-prerelease.16
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.js +1 -1
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +13 -12
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +17 -5
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +24 -20
- 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/css/main.css +43 -30
- package/dist/css/main.css.map +1 -1
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +1 -1
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.js +6 -6
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +13 -12
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +18 -6
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +24 -20
- 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/package.json +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +70 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +1 -1
- package/patternfly-docs/patternfly-docs.config.js +1 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.test.tsx +6 -6
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +1 -0
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +28 -36
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +32 -18
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +74 -60
- package/src/SourcesCard/SourcesCard.scss +17 -0
- package/src/SourcesCard/SourcesCard.test.tsx +93 -0
- package/src/SourcesCard/SourcesCard.tsx +116 -80
|
@@ -17,11 +17,11 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
17
17
|
const menuItems = (_jsxs(_Fragment, { children: [_jsx(DropdownItem, { children: "Rename" }), _jsx(DropdownItem, { children: "Delete" })] }));
|
|
18
18
|
it('should render the dropdown', () => {
|
|
19
19
|
render(_jsx(ChatbotConversationHistoryDropdown, { menuItems: menuItems, menuClassName: "custom-class" }));
|
|
20
|
-
expect(screen.queryByRole('
|
|
20
|
+
expect(screen.queryByRole('menuitem', { name: /Conversation options/i })).toBeInTheDocument();
|
|
21
21
|
});
|
|
22
22
|
it('should display the dropdown menuItems', () => {
|
|
23
23
|
render(_jsx(ChatbotConversationHistoryDropdown, { menuItems: menuItems }));
|
|
24
|
-
const toggle = screen.queryByRole('
|
|
24
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i });
|
|
25
25
|
expect(toggle).toBeInTheDocument();
|
|
26
26
|
fireEvent.click(toggle);
|
|
27
27
|
waitFor(() => {
|
|
@@ -31,14 +31,14 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
31
31
|
});
|
|
32
32
|
it('should invoke onSelect callback when menuitem is clicked', () => {
|
|
33
33
|
render(_jsx(ChatbotConversationHistoryDropdown, { menuItems: menuItems, onSelect: onSelect }));
|
|
34
|
-
const toggle = screen.queryByRole('
|
|
34
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i });
|
|
35
35
|
fireEvent.click(toggle);
|
|
36
36
|
fireEvent.click(screen.getByText('Rename'));
|
|
37
37
|
expect(onSelect).toHaveBeenCalled();
|
|
38
38
|
});
|
|
39
39
|
it('should toggle the dropdown when menuitem is clicked', () => {
|
|
40
40
|
render(_jsx(ChatbotConversationHistoryDropdown, { menuItems: menuItems, onSelect: onSelect }));
|
|
41
|
-
const toggle = screen.queryByRole('
|
|
41
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i });
|
|
42
42
|
fireEvent.click(toggle);
|
|
43
43
|
fireEvent.click(screen.getByText('Delete'));
|
|
44
44
|
expect(onSelect).toHaveBeenCalled();
|
|
@@ -46,7 +46,7 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
46
46
|
});
|
|
47
47
|
it('should close the dropdown when user clicks outside', () => {
|
|
48
48
|
render(_jsx(ChatbotConversationHistoryDropdown, { menuItems: menuItems, onSelect: onSelect }));
|
|
49
|
-
const toggle = screen.queryByRole('
|
|
49
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i });
|
|
50
50
|
fireEvent.click(toggle);
|
|
51
51
|
expect(screen.queryByText('Delete')).toBeInTheDocument();
|
|
52
52
|
fireEvent.click(toggle.parentElement);
|
|
@@ -54,7 +54,7 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
54
54
|
});
|
|
55
55
|
it('should show the tooltip when the user hovers over the toggle button', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
56
56
|
render(_jsx(ChatbotConversationHistoryDropdown, { menuItems: menuItems, label: "Actions dropdown" }));
|
|
57
|
-
const toggle = screen.queryByRole('
|
|
57
|
+
const toggle = screen.queryByRole('menuitem', { name: /Actions dropdown/i });
|
|
58
58
|
fireEvent(toggle, new MouseEvent('mouseenter', {
|
|
59
59
|
bubbles: false,
|
|
60
60
|
cancelable: false
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
|
2
|
-
import { ButtonProps, DrawerProps,
|
|
3
|
-
TitleProps, ListProps, SearchInputProps } from '@patternfly/react-core';
|
|
2
|
+
import { ButtonProps, DrawerProps, DrawerPanelContentProps, DrawerContentProps, DrawerContentBodyProps, DrawerHeadProps, DrawerActionsProps, DrawerCloseButtonProps, DrawerPanelBodyProps, SkeletonProps, MenuProps, TitleProps, MenuListProps, SearchInputProps, MenuItemProps, MenuGroupProps, MenuContentProps } from '@patternfly/react-core';
|
|
4
3
|
import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
|
|
5
4
|
import { HistoryEmptyStateProps } from './EmptyState';
|
|
6
5
|
export interface Conversation {
|
|
@@ -20,10 +19,8 @@ export interface Conversation {
|
|
|
20
19
|
label?: string;
|
|
21
20
|
/** Callback for when user selects item. */
|
|
22
21
|
onSelect?: (event?: React.MouseEvent, value?: string | number) => void;
|
|
23
|
-
/** Additional props passed to
|
|
24
|
-
additionalProps?:
|
|
25
|
-
/** Additional props passed to conversation list item */
|
|
26
|
-
listItemProps?: Omit<ListItemProps, 'children'>;
|
|
22
|
+
/** Additional props passed to menu item */
|
|
23
|
+
additionalProps?: MenuItemProps;
|
|
27
24
|
/** Custom dropdown ID to ensure uniqueness across demo instances */
|
|
28
25
|
dropdownId?: string;
|
|
29
26
|
}
|
|
@@ -43,11 +40,13 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
|
|
|
43
40
|
};
|
|
44
41
|
/** Additional button props for new chat button. */
|
|
45
42
|
newChatButtonProps?: ButtonProps;
|
|
46
|
-
/** Additional props applied to
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
/** Additional props applied to conversation menu group. If conversations is an object, you should pass an object of MenuGroupProps for each group. */
|
|
44
|
+
menuGroupProps?: MenuGroupProps | {
|
|
45
|
+
[key: string]: MenuGroupProps;
|
|
46
|
+
};
|
|
47
|
+
/** Additional props applied to conversation list. If conversations is an object, you should pass an object of MenuListProps for each group. */
|
|
48
|
+
menuListProps?: Omit<MenuListProps, 'children'> | {
|
|
49
|
+
[key: string]: Omit<MenuListProps, 'children'>;
|
|
51
50
|
};
|
|
52
51
|
/** Text shown in blue button */
|
|
53
52
|
newChatButtonText?: string;
|
|
@@ -69,7 +68,7 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
|
|
|
69
68
|
reverseButtonOrder?: boolean;
|
|
70
69
|
/** Custom test id for the drawer actions */
|
|
71
70
|
drawerActionsTestId?: string;
|
|
72
|
-
/**
|
|
71
|
+
/** Additional props applied to menu */
|
|
73
72
|
menuProps?: MenuProps;
|
|
74
73
|
/** Additional props applied to panel */
|
|
75
74
|
drawerPanelContentProps?: DrawerPanelContentProps;
|
|
@@ -105,6 +104,8 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
|
|
|
105
104
|
navTitleProps?: Partial<TitleProps>;
|
|
106
105
|
/** Visually hidden text that gets announced by assistive technologies. Should be used to convey the result count when the search input value changes. */
|
|
107
106
|
searchInputScreenReaderText?: string;
|
|
107
|
+
/** Additional props passed to MenuContent */
|
|
108
|
+
menuContentProps?: Omit<MenuContentProps, 'ref'>;
|
|
108
109
|
}
|
|
109
110
|
export declare const ChatbotConversationHistoryNav: FunctionComponent<ChatbotConversationHistoryNavProps>;
|
|
110
111
|
export default ChatbotConversationHistoryNav;
|
|
@@ -12,28 +12,40 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import { useRef, Fragment } from 'react';
|
|
14
14
|
// Import PatternFly components
|
|
15
|
-
import { Button, Drawer, DrawerPanelContent, DrawerContent, DrawerPanelBody, DrawerHead, DrawerActions, DrawerCloseButton, DrawerContentBody, SearchInput,
|
|
15
|
+
import { Button, Drawer, DrawerPanelContent, DrawerContent, DrawerPanelBody, DrawerHead, DrawerActions, DrawerCloseButton, DrawerContentBody, SearchInput, Title, Icon, MenuList, MenuGroup, MenuItem, Menu, MenuContent } from '@patternfly/react-core';
|
|
16
16
|
import { OutlinedClockIcon, OutlinedCommentAltIcon, PenToSquareIcon } from '@patternfly/react-icons';
|
|
17
17
|
import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
|
|
18
18
|
import ConversationHistoryDropdown from './ChatbotConversationHistoryDropdown';
|
|
19
19
|
import LoadingState from './LoadingState';
|
|
20
20
|
import HistoryEmptyState from './EmptyState';
|
|
21
21
|
export const ChatbotConversationHistoryNav = (_a) => {
|
|
22
|
-
var { onDrawerToggle, isDrawerOpen, setIsDrawerOpen, activeItemId, onSelectActiveItem, conversations,
|
|
22
|
+
var { onDrawerToggle, isDrawerOpen, setIsDrawerOpen, activeItemId, onSelectActiveItem, conversations, menuListProps, newChatButtonText = 'New chat', drawerContent, onNewChat, newChatButtonProps, searchInputPlaceholder = 'Search previous conversations...', searchInputAriaLabel = 'Search previous conversations', searchInputProps, handleTextInputChange, displayMode, reverseButtonOrder = false, drawerActionsTestId = 'chatbot-nav-drawer-actions', drawerPanelContentProps, drawerContentProps, drawerContentBodyProps, drawerHeadProps, drawerActionsProps, drawerCloseButtonProps, drawerPanelBodyProps, isLoading, loadingState, errorState, emptyState, noResultsState, isCompact, title = 'Chat history', navTitleProps, navTitleIcon = _jsx(OutlinedClockIcon, {}), searchInputScreenReaderText, menuProps, menuGroupProps, menuContentProps } = _a, props = __rest(_a, ["onDrawerToggle", "isDrawerOpen", "setIsDrawerOpen", "activeItemId", "onSelectActiveItem", "conversations", "menuListProps", "newChatButtonText", "drawerContent", "onNewChat", "newChatButtonProps", "searchInputPlaceholder", "searchInputAriaLabel", "searchInputProps", "handleTextInputChange", "displayMode", "reverseButtonOrder", "drawerActionsTestId", "drawerPanelContentProps", "drawerContentProps", "drawerContentBodyProps", "drawerHeadProps", "drawerActionsProps", "drawerCloseButtonProps", "drawerPanelBodyProps", "isLoading", "loadingState", "errorState", "emptyState", "noResultsState", "isCompact", "title", "navTitleProps", "navTitleIcon", "searchInputScreenReaderText", "menuProps", "menuGroupProps", "menuContentProps"]);
|
|
23
23
|
const drawerRef = useRef(null);
|
|
24
24
|
const onExpand = () => {
|
|
25
25
|
drawerRef.current && drawerRef.current.focus();
|
|
26
26
|
};
|
|
27
|
+
const isConversation = (item) => item && typeof item === 'object' && 'id' in item && 'text' in item;
|
|
27
28
|
const getNavItem = (conversation) => {
|
|
28
29
|
var _a;
|
|
29
|
-
return (_jsx(
|
|
30
|
+
return (_jsx(MenuItem, Object.assign({ className: `pf-chatbot__menu-item ${activeItemId && activeItemId === conversation.id ? 'pf-chatbot__menu-item--active' : ''}`, itemId: conversation.id }, (conversation.noIcon ? {} : { icon: (_a = conversation.icon) !== null && _a !== void 0 ? _a : _jsx(OutlinedCommentAltIcon, {}) }), (conversation.menuItems
|
|
31
|
+
? {
|
|
32
|
+
actions: (_jsx(ConversationHistoryDropdown, { menuClassName: conversation.menuClassName, onSelect: conversation.onSelect, menuItems: conversation.menuItems, label: conversation.label }))
|
|
33
|
+
}
|
|
34
|
+
: {}), conversation.additionalProps, { children: conversation.text })));
|
|
30
35
|
};
|
|
31
36
|
const buildConversations = () => {
|
|
32
37
|
if (Array.isArray(conversations)) {
|
|
33
|
-
return (_jsx(
|
|
38
|
+
return (_jsx(MenuList, Object.assign({}, menuListProps, { children: conversations.map((conversation) => {
|
|
39
|
+
if (isConversation(conversation)) {
|
|
40
|
+
return _jsx(Fragment, { children: getNavItem(conversation) }, conversation.id);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return conversation;
|
|
44
|
+
}
|
|
45
|
+
}) })));
|
|
34
46
|
}
|
|
35
47
|
else {
|
|
36
|
-
return (_jsx(
|
|
48
|
+
return (_jsx(_Fragment, { children: Object.keys(conversations).map((navGroup) => (_jsx(MenuGroup, Object.assign({ className: "pf-chatbot__menu-item-header", label: navGroup, labelHeadingLevel: "h3" }, menuGroupProps === null || menuGroupProps === void 0 ? void 0 : menuGroupProps[navGroup], { children: _jsx(MenuList, Object.assign({}, menuListProps === null || menuListProps === void 0 ? void 0 : menuListProps[navGroup], { children: conversations[navGroup].map((conversation) => (_jsx(Fragment, { children: getNavItem(conversation) }, conversation.id))) })) }), navGroup))) }));
|
|
37
49
|
}
|
|
38
50
|
};
|
|
39
51
|
// Menu Content
|
|
@@ -49,7 +61,7 @@ export const ChatbotConversationHistoryNav = (_a) => {
|
|
|
49
61
|
if (noResultsState) {
|
|
50
62
|
return _jsx(HistoryEmptyState, Object.assign({}, noResultsState));
|
|
51
63
|
}
|
|
52
|
-
return _jsx(
|
|
64
|
+
return (_jsx(Menu, Object.assign({ isPlain: true, onSelect: onSelectActiveItem, activeItemId: activeItemId }, menuProps, { children: _jsx(MenuContent, Object.assign({}, menuContentProps, { children: buildConversations() })) })));
|
|
53
65
|
};
|
|
54
66
|
const renderDrawerContent = () => (_jsx(_Fragment, { children: _jsx(DrawerPanelBody, Object.assign({}, drawerPanelBodyProps, { children: renderMenuContent() })) }));
|
|
55
67
|
const renderPanelContent = () => {
|
|
@@ -193,26 +193,30 @@ describe('ChatbotConversationHistoryNav', () => {
|
|
|
193
193
|
const iconElement = container.querySelector('.pf-chatbot__title-icon');
|
|
194
194
|
expect(iconElement).toBeInTheDocument();
|
|
195
195
|
});
|
|
196
|
-
it('Passes
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
expect(screen.getByRole('
|
|
204
|
-
});
|
|
205
|
-
it('Passes
|
|
206
|
-
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations,
|
|
207
|
-
expect(screen.getByRole('
|
|
208
|
-
});
|
|
209
|
-
it('Passes
|
|
210
|
-
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations },
|
|
211
|
-
expect(screen.getByRole('
|
|
212
|
-
});
|
|
213
|
-
it('Passes
|
|
214
|
-
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations:
|
|
215
|
-
expect(screen.getByRole('
|
|
196
|
+
it('Passes menuProps to Menu', () => {
|
|
197
|
+
var _a;
|
|
198
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, menuProps: { className: 'test' } }));
|
|
199
|
+
expect((_a = screen.getByRole('menu').parentElement) === null || _a === void 0 ? void 0 : _a.parentElement).toHaveClass('test');
|
|
200
|
+
});
|
|
201
|
+
it('Passes menuContentProps to MenuContent', () => {
|
|
202
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, menuContentProps: { className: 'test' } }));
|
|
203
|
+
expect(screen.getByRole('menu').parentElement).toHaveClass('test');
|
|
204
|
+
});
|
|
205
|
+
it('Passes menuListProps to MenuList when conversations is an array', () => {
|
|
206
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: initialConversations, menuListProps: { className: 'test' } }));
|
|
207
|
+
expect(screen.getByRole('menu')).toHaveClass('test');
|
|
208
|
+
});
|
|
209
|
+
it('Passes menuListProps to MenuList when conversations is an object', () => {
|
|
210
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations }, menuListProps: { Today: { className: 'test' } } }));
|
|
211
|
+
expect(screen.getByRole('menu')).toHaveClass('test');
|
|
212
|
+
});
|
|
213
|
+
it('Passes menuGroupProps to MenuGroup when conversations is an object', () => {
|
|
214
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: { Today: initialConversations }, menuGroupProps: { Today: { className: 'test' } } }));
|
|
215
|
+
expect(screen.getByRole('menu').parentElement).toHaveClass('test');
|
|
216
|
+
});
|
|
217
|
+
it('Passes additionalProps to MenuItem', () => {
|
|
218
|
+
render(_jsx(ChatbotConversationHistoryNav, { onDrawerToggle: onDrawerToggle, isDrawerOpen: true, displayMode: ChatbotDisplayMode.fullscreen, setIsDrawerOpen: jest.fn(), conversations: [{ id: '1', text: 'ChatBot documentation', additionalProps: { className: 'test' } }] }));
|
|
219
|
+
expect(screen.getByRole('menuitem')).toHaveClass('test');
|
|
216
220
|
});
|
|
217
221
|
it('should be able to spread search input props when searchInputProps is passed', () => {
|
|
218
222
|
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' } }));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FunctionComponent } from 'react';
|
|
2
|
-
import { ButtonProps, CardProps } from '@patternfly/react-core';
|
|
2
|
+
import { ButtonProps, CardBodyProps, CardFooterProps, CardProps, CardTitleProps, TruncateProps } from '@patternfly/react-core';
|
|
3
3
|
export interface SourcesCardProps extends CardProps {
|
|
4
4
|
/** Additional classes for the pagination navigation container. */
|
|
5
5
|
className?: string;
|
|
@@ -13,6 +13,8 @@ export interface SourcesCardProps extends CardProps {
|
|
|
13
13
|
sources: {
|
|
14
14
|
/** Title of sources card */
|
|
15
15
|
title?: string;
|
|
16
|
+
/** Subtitle of sources card */
|
|
17
|
+
subtitle?: string;
|
|
16
18
|
/** Link to source */
|
|
17
19
|
link: string;
|
|
18
20
|
/** Body of sources card */
|
|
@@ -25,6 +27,10 @@ export interface SourcesCardProps extends CardProps {
|
|
|
25
27
|
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
26
28
|
/** Any additional props applied to the title of the Sources card */
|
|
27
29
|
titleProps?: ButtonProps;
|
|
30
|
+
/** Custom footer applied to the Sources card */
|
|
31
|
+
footer?: React.ReactNode;
|
|
32
|
+
/** Additional props passed to Truncate component */
|
|
33
|
+
truncateProps?: TruncateProps;
|
|
28
34
|
}[];
|
|
29
35
|
/** Label for the English word "source" */
|
|
30
36
|
sourceWord?: string;
|
|
@@ -44,6 +50,12 @@ export interface SourcesCardProps extends CardProps {
|
|
|
44
50
|
showMoreWords?: string;
|
|
45
51
|
/** Label for English words "show less" */
|
|
46
52
|
showLessWords?: string;
|
|
53
|
+
/** Additional props passed to card title */
|
|
54
|
+
cardTitleProps?: CardTitleProps;
|
|
55
|
+
/** Additional props passed to card body */
|
|
56
|
+
cardBodyProps?: CardBodyProps;
|
|
57
|
+
/** Additional props passed to card footer */
|
|
58
|
+
cardFooterProps?: CardFooterProps;
|
|
47
59
|
}
|
|
48
60
|
declare const SourcesCard: FunctionComponent<SourcesCardProps>;
|
|
49
61
|
export default SourcesCard;
|
|
@@ -16,7 +16,7 @@ import { Button, ButtonVariant, Card, CardBody, CardFooter, CardTitle, Expandabl
|
|
|
16
16
|
import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
|
|
17
17
|
const SourcesCard = (_a) => {
|
|
18
18
|
var _b;
|
|
19
|
-
var { className, isDisabled, paginationAriaLabel = 'Pagination', sources, sourceWord = 'source', sourceWordPlural = 'sources', toNextPageAriaLabel = 'Go to next page', toPreviousPageAriaLabel = 'Go to previous page', onNextClick, onPreviousClick, onSetPage, showMoreWords = 'show more', showLessWords = 'show less', isCompact } = _a, props = __rest(_a, ["className", "isDisabled", "paginationAriaLabel", "sources", "sourceWord", "sourceWordPlural", "toNextPageAriaLabel", "toPreviousPageAriaLabel", "onNextClick", "onPreviousClick", "onSetPage", "showMoreWords", "showLessWords", "isCompact"]);
|
|
19
|
+
var { className, isDisabled, paginationAriaLabel = 'Pagination', sources, sourceWord = 'source', sourceWordPlural = 'sources', toNextPageAriaLabel = 'Go to next page', toPreviousPageAriaLabel = 'Go to previous page', onNextClick, onPreviousClick, onSetPage, showMoreWords = 'show more', showLessWords = 'show less', isCompact, cardTitleProps, cardBodyProps, cardFooterProps } = _a, props = __rest(_a, ["className", "isDisabled", "paginationAriaLabel", "sources", "sourceWord", "sourceWordPlural", "toNextPageAriaLabel", "toPreviousPageAriaLabel", "onNextClick", "onPreviousClick", "onSetPage", "showMoreWords", "showLessWords", "isCompact", "cardTitleProps", "cardBodyProps", "cardFooterProps"]);
|
|
20
20
|
const [page, setPage] = useState(1);
|
|
21
21
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
22
22
|
const onToggle = (_event, isExpanded) => {
|
|
@@ -26,15 +26,15 @@ const SourcesCard = (_a) => {
|
|
|
26
26
|
setPage(newPage);
|
|
27
27
|
onSetPage && onSetPage(_evt, newPage);
|
|
28
28
|
};
|
|
29
|
-
const renderTitle = (title) => {
|
|
29
|
+
const renderTitle = (title, truncateProps) => {
|
|
30
30
|
if (title) {
|
|
31
|
-
return _jsx(Truncate, { content: title });
|
|
31
|
+
return _jsx(Truncate, Object.assign({ content: title }, truncateProps));
|
|
32
32
|
}
|
|
33
33
|
return `Source ${page}`;
|
|
34
34
|
};
|
|
35
|
-
return (_jsxs("div", { className: "pf-chatbot__source", children: [_jsx("span", { children: pluralize(sources.length, sourceWord, sourceWordPlural) }), _jsxs(Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [_jsx(CardTitle, { className: "pf-chatbot__sources-card-title", children: _jsx(Button, Object.assign({ component: "a", variant: ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? _jsx(ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, onClick: (_b = sources[page - 1].onClick) !== null && _b !== void 0 ? _b : undefined }, sources[page - 1].titleProps, { children: renderTitle(sources[page - 1].title) })) }), sources[page - 1].body && (_jsx(CardBody, { className: `pf-chatbot__sources-card-body
|
|
35
|
+
return (_jsxs("div", { className: "pf-chatbot__source", children: [_jsx("span", { children: pluralize(sources.length, sourceWord, sourceWordPlural) }), _jsxs(Card, Object.assign({ isCompact: isCompact, className: "pf-chatbot__sources-card" }, props, { children: [_jsx(CardTitle, Object.assign({ className: "pf-chatbot__sources-card-title" }, cardTitleProps, { children: _jsxs("div", { className: "pf-chatbot__sources-card-title-container", children: [_jsx(Button, Object.assign({ component: "a", variant: ButtonVariant.link, href: sources[page - 1].link, icon: sources[page - 1].isExternal ? _jsx(ExternalLinkSquareAltIcon, {}) : undefined, iconPosition: "end", isInline: true, rel: sources[page - 1].isExternal ? 'noreferrer' : undefined, target: sources[page - 1].isExternal ? '_blank' : undefined, onClick: (_b = sources[page - 1].onClick) !== null && _b !== void 0 ? _b : undefined }, sources[page - 1].titleProps, { children: renderTitle(sources[page - 1].title, sources[page - 1].truncateProps) })), sources[page - 1].subtitle && (_jsx("span", { className: "pf-chatbot__sources-card-subtitle", children: sources[page - 1].subtitle }))] }) })), sources[page - 1].body && (_jsx(CardBody, Object.assign({ className: `pf-chatbot__sources-card-body ${sources[page - 1].footer ? 'pf-chatbot__compact-sources-card-body' : undefined}` }, cardBodyProps, { children: sources[page - 1].hasShowMore ? (
|
|
36
36
|
// prevents extra VO announcements of button text - parent Message has aria-live
|
|
37
|
-
_jsx("div", { "aria-live": "off", children: _jsx(ExpandableSection, { variant: ExpandableSectionVariant.truncate, toggleText: isExpanded ? showLessWords : showMoreWords, onToggle: onToggle, isExpanded: isExpanded, truncateMaxLines: 2, children: sources[page - 1].body }) })) : (_jsx("div", { className: "pf-chatbot__sources-card-body-text", children: sources[page - 1].body })) })), sources.length > 1 && (_jsx(CardFooter, { className: "pf-chatbot__sources-card-footer-container", children: _jsx("div", { className: "pf-chatbot__sources-card-footer", children: _jsxs("nav", { className: `pf-chatbot__sources-card-footer-buttons ${className}`, "aria-label": paginationAriaLabel, children: [_jsx(Button, { variant: ButtonVariant.plain, isDisabled: isDisabled || page === 1, "data-action": "previous", onClick: (event) => {
|
|
37
|
+
_jsx("div", { "aria-live": "off", children: _jsx(ExpandableSection, { variant: ExpandableSectionVariant.truncate, toggleText: isExpanded ? showLessWords : showMoreWords, onToggle: onToggle, isExpanded: isExpanded, truncateMaxLines: 2, children: sources[page - 1].body }) })) : (_jsx("div", { className: "pf-chatbot__sources-card-body-text", children: sources[page - 1].body })) }))), sources[page - 1].footer ? (_jsx(CardFooter, Object.assign({ className: "pf-chatbot__sources-card-footer" }, cardFooterProps, { children: sources[page - 1].footer }))) : (sources.length > 1 && (_jsx(CardFooter, Object.assign({ className: "pf-chatbot__sources-card-footer-container" }, cardFooterProps, { children: _jsx("div", { className: "pf-chatbot__sources-card-footer", children: _jsxs("nav", { className: `pf-chatbot__sources-card-footer-buttons ${className}`, "aria-label": paginationAriaLabel, children: [_jsx(Button, { variant: ButtonVariant.plain, isDisabled: isDisabled || page === 1, "data-action": "previous", onClick: (event) => {
|
|
38
38
|
const newPage = page >= 1 ? page - 1 : 1;
|
|
39
39
|
onPreviousClick && onPreviousClick(event, newPage);
|
|
40
40
|
handleNewPage(event, newPage);
|
|
@@ -42,6 +42,6 @@ const SourcesCard = (_a) => {
|
|
|
42
42
|
const newPage = page + 1 <= sources.length ? page + 1 : sources.length;
|
|
43
43
|
onNextClick && onNextClick(event, newPage);
|
|
44
44
|
handleNewPage(event, newPage);
|
|
45
|
-
}, children: _jsx(Icon, { isInline: true, iconSize: "lg", children: _jsx("svg", { className: "pf-v6-svg", viewBox: "0 0 180 500", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: _jsx("path", { d: "M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z" }) }) }) })] }) }) }))] }))] }));
|
|
45
|
+
}, children: _jsx(Icon, { isInline: true, iconSize: "lg", children: _jsx("svg", { className: "pf-v6-svg", viewBox: "0 0 180 500", fill: "currentColor", "aria-hidden": "true", role: "img", width: "1em", height: "1em", children: _jsx("path", { d: "M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z" }) }) }) })] }) }) }))))] }))] }));
|
|
46
46
|
};
|
|
47
47
|
export default SourcesCard;
|
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
11
|
import { render, screen } from '@testing-library/react';
|
|
12
12
|
import userEvent from '@testing-library/user-event';
|
|
13
13
|
import '@testing-library/jest-dom';
|
|
@@ -179,4 +179,53 @@ describe('SourcesCard', () => {
|
|
|
179
179
|
render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', titleProps: { className: 'test' } }] }));
|
|
180
180
|
expect(screen.getByRole('link', { name: /How to make an apple pie/i })).toHaveClass('test');
|
|
181
181
|
});
|
|
182
|
+
it('should apply cardTitleProps appropriately', () => {
|
|
183
|
+
render(_jsx(SourcesCard, { cardTitleProps: { 'data-testid': 'card-title', className: 'test' }, sources: [{ title: 'How to make an apple pie', link: '' }] }));
|
|
184
|
+
expect(screen.getByTestId('card-title')).toHaveClass('test');
|
|
185
|
+
});
|
|
186
|
+
it('should apply cardBodyProps appropriately', () => {
|
|
187
|
+
render(_jsx(SourcesCard, { cardBodyProps: { 'data-testid': 'card-body', body: 'To make an apple pie, you must first...', className: 'test' }, sources: [{ title: 'How to make an apple pie', link: '', body: 'To make an apple pie, you must first...' }] }));
|
|
188
|
+
expect(screen.getByTestId('card-body')).toHaveClass('test');
|
|
189
|
+
});
|
|
190
|
+
it('should apply cardFooterProps appropriately', () => {
|
|
191
|
+
render(_jsx(SourcesCard, { cardFooterProps: { 'data-testid': 'card-footer', className: 'test' }, sources: [
|
|
192
|
+
{ title: 'How to make an apple pie', link: '' },
|
|
193
|
+
{ title: 'How to make cookies', link: '' }
|
|
194
|
+
] }));
|
|
195
|
+
expect(screen.getByTestId('card-footer')).toHaveClass('test');
|
|
196
|
+
});
|
|
197
|
+
it('should apply truncateProps appropriately', () => {
|
|
198
|
+
render(_jsx(SourcesCard, { sources: [
|
|
199
|
+
{
|
|
200
|
+
title: 'How to make an apple pie',
|
|
201
|
+
link: '',
|
|
202
|
+
truncateProps: { 'data-testid': 'card-truncate', className: 'test' }
|
|
203
|
+
}
|
|
204
|
+
] }));
|
|
205
|
+
expect(screen.getByTestId('card-truncate')).toHaveClass('test');
|
|
206
|
+
});
|
|
207
|
+
it('should apply custom footer appropriately when there is one source', () => {
|
|
208
|
+
render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', footer: _jsx(_Fragment, { children: "I am a custom footer" }) }] }));
|
|
209
|
+
expect(screen.getByText('I am a custom footer'));
|
|
210
|
+
expect(screen.queryByText('1/1')).toBeFalsy();
|
|
211
|
+
});
|
|
212
|
+
it('should apply custom footer appropriately when are multiple sources', () => {
|
|
213
|
+
render(_jsx(SourcesCard, { sources: [
|
|
214
|
+
{ title: 'How to make an apple pie', link: '', footer: _jsx(_Fragment, { children: "I am a custom footer" }) },
|
|
215
|
+
{ title: 'How to bake bread', link: '' }
|
|
216
|
+
] }));
|
|
217
|
+
expect(screen.getByText('I am a custom footer'));
|
|
218
|
+
// does not show navigation bar
|
|
219
|
+
expect(screen.queryByText('1/2')).toBeFalsy();
|
|
220
|
+
});
|
|
221
|
+
it('should apply footer props to custom footer appropriately', () => {
|
|
222
|
+
render(_jsx(SourcesCard, { cardFooterProps: { 'data-testid': 'card-footer', className: 'test' }, sources: [{ title: 'How to make an apple pie', link: '', footer: _jsx(_Fragment, { children: "I am a custom footer" }) }] }));
|
|
223
|
+
expect(screen.getByText('I am a custom footer'));
|
|
224
|
+
expect(screen.getByTestId('card-footer')).toHaveClass('test');
|
|
225
|
+
});
|
|
226
|
+
it('should apply subtitle appropriately', () => {
|
|
227
|
+
render(_jsx(SourcesCard, { sources: [{ title: 'How to make an apple pie', link: '', subtitle: 'You must first create the universe' }] }));
|
|
228
|
+
expect(screen.getByText('How to make an apple pie'));
|
|
229
|
+
expect(screen.getByText('You must first create the universe'));
|
|
230
|
+
});
|
|
182
231
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/chatbot",
|
|
3
|
-
"version": "6.4.0-prerelease.
|
|
3
|
+
"version": "6.4.0-prerelease.16",
|
|
4
4
|
"description": "This library provides React components based on PatternFly 6 that can be used to build chatbots.",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"react-dom": "^18 || ^19"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@patternfly/documentation-framework": "6.
|
|
56
|
+
"@patternfly/documentation-framework": "6.19.0",
|
|
57
57
|
"@patternfly/patternfly": "^6.1.0",
|
|
58
58
|
"@patternfly/patternfly-a11y": "^5.0.0",
|
|
59
59
|
"@types/dom-speech-recognition": "^0.0.4",
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { FunctionComponent, MouseEvent as ReactMouseEvent, KeyboardEvent as ReactKeyboardEvent } from 'react';
|
|
2
2
|
import Message from '@patternfly/chatbot/dist/dynamic/Message';
|
|
3
3
|
import patternflyAvatar from './patternfly_avatar.jpg';
|
|
4
|
+
import { Button, Flex, FlexItem, Label, Popover } from '@patternfly/react-core';
|
|
5
|
+
import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons';
|
|
4
6
|
|
|
5
7
|
export const MessageWithSourcesExample: FunctionComponent = () => {
|
|
6
8
|
const onSetPage = (_event: ReactMouseEvent | ReactKeyboardEvent | MouseEvent, newPage: number) => {
|
|
@@ -8,8 +10,76 @@ export const MessageWithSourcesExample: FunctionComponent = () => {
|
|
|
8
10
|
console.log(`Page changed to ${newPage}`);
|
|
9
11
|
};
|
|
10
12
|
|
|
13
|
+
const date = new Date();
|
|
14
|
+
|
|
15
|
+
const datePart = date.toLocaleDateString('en', {
|
|
16
|
+
year: 'numeric',
|
|
17
|
+
month: 'short',
|
|
18
|
+
day: 'numeric'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const timePart = date.toLocaleTimeString('en', {
|
|
22
|
+
hour: '2-digit',
|
|
23
|
+
minute: '2-digit',
|
|
24
|
+
hour12: true
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const formattedDate = `${datePart}, ${timePart}`;
|
|
28
|
+
|
|
11
29
|
return (
|
|
12
30
|
<>
|
|
31
|
+
<Message
|
|
32
|
+
name="Bot"
|
|
33
|
+
role="bot"
|
|
34
|
+
avatar={patternflyAvatar}
|
|
35
|
+
content="This example has a custom subtitle and footer with no pagination"
|
|
36
|
+
sources={{
|
|
37
|
+
sources: [
|
|
38
|
+
{
|
|
39
|
+
title: 'Getting started with Red Hat OpenShift',
|
|
40
|
+
subtitle: 'Red Hat knowledge base',
|
|
41
|
+
link: '#',
|
|
42
|
+
body: 'Red Hat OpenShift on IBM Cloud is a managed offering to create your own cluster of compute hosts where you can deploy and manage containerized apps on IBM Cloud ...',
|
|
43
|
+
isExternal: true,
|
|
44
|
+
footer: (
|
|
45
|
+
<Flex className="pf-chatbot__sources-card-subtle" gap={{ default: 'gapXs' }}>
|
|
46
|
+
<FlexItem alignSelf={{ default: 'alignSelfStretch' }}>
|
|
47
|
+
<Flex justifyContent={{ default: 'justifyContentSpaceBetween' }}>
|
|
48
|
+
<FlexItem>
|
|
49
|
+
<Label color="green">Confidence 93%</Label>
|
|
50
|
+
</FlexItem>
|
|
51
|
+
<FlexItem>
|
|
52
|
+
<Popover
|
|
53
|
+
headerContent={
|
|
54
|
+
<Flex gap={{ default: 'gapXs' }}>
|
|
55
|
+
<FlexItem>
|
|
56
|
+
<OutlinedQuestionCircleIcon />
|
|
57
|
+
</FlexItem>
|
|
58
|
+
<FlexItem>Why this confidence score?</FlexItem>
|
|
59
|
+
</Flex>
|
|
60
|
+
}
|
|
61
|
+
bodyContent={
|
|
62
|
+
<>
|
|
63
|
+
A high confidence score indicates a strong match. The system found significant overlap in
|
|
64
|
+
key data points, including text content, names, dates, and organizational details, with a
|
|
65
|
+
high degree of certainty. This match is highly reliable.
|
|
66
|
+
</>
|
|
67
|
+
}
|
|
68
|
+
>
|
|
69
|
+
<Button variant="link" icon={<OutlinedQuestionCircleIcon />}>
|
|
70
|
+
Learn about this score
|
|
71
|
+
</Button>
|
|
72
|
+
</Popover>
|
|
73
|
+
</FlexItem>
|
|
74
|
+
</Flex>
|
|
75
|
+
</FlexItem>
|
|
76
|
+
<FlexItem>{`Last updated: ${formattedDate}`}</FlexItem>
|
|
77
|
+
</Flex>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}}
|
|
82
|
+
/>
|
|
13
83
|
<Message
|
|
14
84
|
name="Bot"
|
|
15
85
|
role="bot"
|
|
@@ -34,7 +34,7 @@ import Message from '@patternfly/chatbot/dist/dynamic/Message';
|
|
|
34
34
|
import MessageDivider from '@patternfly/chatbot/dist/dynamic/MessageDivider';
|
|
35
35
|
import { rehypeCodeBlockToggle } from '@patternfly/chatbot/dist/esm/Message/Plugins/rehypeCodeBlockToggle';
|
|
36
36
|
import SourcesCard from '@patternfly/chatbot/dist/dynamic/SourcesCard';
|
|
37
|
-
import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, RedoIcon, RobotIcon } from '@patternfly/react-icons';
|
|
37
|
+
import { ArrowCircleDownIcon, ArrowRightIcon, CheckCircleIcon, CubeIcon, CubesIcon, DownloadIcon, InfoCircleIcon, OutlinedQuestionCircleIcon, RedoIcon, RobotIcon } from '@patternfly/react-icons';
|
|
38
38
|
import patternflyAvatar from './patternfly_avatar.jpg';
|
|
39
39
|
import AttachmentEdit from '@patternfly/chatbot/dist/dynamic/AttachmentEdit';
|
|
40
40
|
import FileDetails from '@patternfly/chatbot/dist/dynamic/FileDetails';
|
|
@@ -14,13 +14,13 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
14
14
|
|
|
15
15
|
it('should render the dropdown', () => {
|
|
16
16
|
render(<ChatbotConversationHistoryDropdown menuItems={menuItems} menuClassName="custom-class" />);
|
|
17
|
-
expect(screen.queryByRole('
|
|
17
|
+
expect(screen.queryByRole('menuitem', { name: /Conversation options/i })).toBeInTheDocument();
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
it('should display the dropdown menuItems', () => {
|
|
21
21
|
render(<ChatbotConversationHistoryDropdown menuItems={menuItems} />);
|
|
22
22
|
|
|
23
|
-
const toggle = screen.queryByRole('
|
|
23
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i })!;
|
|
24
24
|
|
|
25
25
|
expect(toggle).toBeInTheDocument();
|
|
26
26
|
fireEvent.click(toggle);
|
|
@@ -33,7 +33,7 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
33
33
|
|
|
34
34
|
it('should invoke onSelect callback when menuitem is clicked', () => {
|
|
35
35
|
render(<ChatbotConversationHistoryDropdown menuItems={menuItems} onSelect={onSelect} />);
|
|
36
|
-
const toggle = screen.queryByRole('
|
|
36
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i })!;
|
|
37
37
|
fireEvent.click(toggle);
|
|
38
38
|
fireEvent.click(screen.getByText('Rename'));
|
|
39
39
|
|
|
@@ -42,7 +42,7 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
42
42
|
|
|
43
43
|
it('should toggle the dropdown when menuitem is clicked', () => {
|
|
44
44
|
render(<ChatbotConversationHistoryDropdown menuItems={menuItems} onSelect={onSelect} />);
|
|
45
|
-
const toggle = screen.queryByRole('
|
|
45
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i })!;
|
|
46
46
|
fireEvent.click(toggle);
|
|
47
47
|
fireEvent.click(screen.getByText('Delete'));
|
|
48
48
|
|
|
@@ -53,7 +53,7 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
53
53
|
|
|
54
54
|
it('should close the dropdown when user clicks outside', () => {
|
|
55
55
|
render(<ChatbotConversationHistoryDropdown menuItems={menuItems} onSelect={onSelect} />);
|
|
56
|
-
const toggle = screen.queryByRole('
|
|
56
|
+
const toggle = screen.queryByRole('menuitem', { name: /Conversation options/i })!;
|
|
57
57
|
fireEvent.click(toggle);
|
|
58
58
|
|
|
59
59
|
expect(screen.queryByText('Delete')).toBeInTheDocument();
|
|
@@ -64,7 +64,7 @@ describe('ChatbotConversationHistoryDropdown', () => {
|
|
|
64
64
|
|
|
65
65
|
it('should show the tooltip when the user hovers over the toggle button', async () => {
|
|
66
66
|
render(<ChatbotConversationHistoryDropdown menuItems={menuItems} label="Actions dropdown" />);
|
|
67
|
-
const toggle = screen.queryByRole('
|
|
67
|
+
const toggle = screen.queryByRole('menuitem', { name: /Actions dropdown/i })!;
|
|
68
68
|
|
|
69
69
|
fireEvent(
|
|
70
70
|
toggle,
|