@patternfly/chatbot 2.2.0-prerelease.26 → 2.2.0-prerelease.28
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 +3 -3
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +11 -4
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +22 -10
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +60 -0
- package/dist/cjs/ChatbotConversationHistoryNav/EmptyState.d.ts +11 -0
- package/dist/cjs/ChatbotConversationHistoryNav/EmptyState.js +29 -0
- package/dist/cjs/ChatbotConversationHistoryNav/LoadingState.d.ts +4 -0
- package/dist/cjs/ChatbotConversationHistoryNav/LoadingState.js +45 -0
- package/dist/cjs/ChatbotFooter/ChatbotFootnote.d.ts +1 -1
- package/dist/cjs/ChatbotFooter/ChatbotFootnote.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderSelectorDropdown.js +3 -3
- package/dist/cjs/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.js +2 -2
- package/dist/cjs/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.js +19 -11
- package/dist/cjs/ResponseActions/ResponseActions.test.js +2 -2
- package/dist/css/main.css +17 -3
- package/dist/css/main.css.map +1 -1
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.d.ts +3 -3
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +11 -4
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +22 -10
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +60 -0
- package/dist/esm/ChatbotConversationHistoryNav/EmptyState.d.ts +11 -0
- package/dist/esm/ChatbotConversationHistoryNav/EmptyState.js +22 -0
- package/dist/esm/ChatbotConversationHistoryNav/LoadingState.d.ts +4 -0
- package/dist/esm/ChatbotConversationHistoryNav/LoadingState.js +38 -0
- package/dist/esm/ChatbotFooter/ChatbotFootnote.d.ts +1 -1
- package/dist/esm/ChatbotFooter/ChatbotFootnote.js +1 -1
- package/dist/esm/ChatbotHeader/ChatbotHeaderSelectorDropdown.js +3 -3
- package/dist/esm/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.js +2 -2
- package/dist/esm/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.js +19 -11
- package/dist/esm/ResponseActions/ResponseActions.test.js +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/AttachmentError.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +19 -6
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/FileDropZone.tsx +2 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithAttachment.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithCustomResponseActions.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickResponses.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickStart.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithResponseActions.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotFootnote.tsx +4 -4
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +34 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotWelcomeInteraction.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotWelcomePrompt.tsx +7 -7
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx +10 -10
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachment.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachmentMenu.tsx +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedChatbot.tsx +10 -10
- package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx +1 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +3 -3
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +14 -0
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +130 -0
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +60 -25
- package/src/ChatbotConversationHistoryNav/EmptyState.tsx +44 -0
- package/src/ChatbotConversationHistoryNav/LoadingState.tsx +38 -0
- package/src/ChatbotFooter/ChatbotFootnote.tsx +2 -2
- package/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.test.tsx +2 -2
- package/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx +3 -3
- package/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.test.tsx +26 -18
- package/src/ChatbotWelcomePrompt/__snapshots__/ChatbotWelcomePrompt.test.tsx.snap +2 -2
- package/src/MessageBar/MessageBar.scss +3 -3
- package/src/ResponseActions/ResponseActions.test.tsx +4 -2
@@ -3,7 +3,7 @@ import { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
|
|
3
3
|
import ChatbotConversationHistoryNav, {
|
4
4
|
Conversation
|
5
5
|
} from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
|
6
|
-
import { Checkbox } from '@patternfly/react-core';
|
6
|
+
import { Checkbox, EmptyStateStatus, Spinner } from '@patternfly/react-core';
|
7
7
|
|
8
8
|
const initialConversations: { [key: string]: Conversation[] } = {
|
9
9
|
Today: [{ id: '1', text: 'Red Hat products and services' }],
|
@@ -31,12 +31,29 @@ const initialConversations: { [key: string]: Conversation[] } = {
|
|
31
31
|
]
|
32
32
|
};
|
33
33
|
|
34
|
+
const ERROR = {
|
35
|
+
bodyText: (
|
36
|
+
<>
|
37
|
+
To try again, check your connection and reload this page. If the issue persists,{' '}
|
38
|
+
<a href="">contact the support team</a>.
|
39
|
+
</>
|
40
|
+
),
|
41
|
+
buttonText: 'Reload',
|
42
|
+
buttonIcon: <Spinner size="sm" />,
|
43
|
+
hasButton: true,
|
44
|
+
titleText: 'Could not load chat history',
|
45
|
+
status: EmptyStateStatus.danger,
|
46
|
+
onClick: () => alert('Clicked Reload')
|
47
|
+
};
|
48
|
+
|
34
49
|
export const ChatbotHeaderTitleDemo: React.FunctionComponent = () => {
|
35
50
|
const [isOpen, setIsOpen] = React.useState(true);
|
36
51
|
const [isButtonOrderReversed, setIsButtonOrderReversed] = React.useState(false);
|
37
52
|
const [conversations, setConversations] = React.useState<Conversation[] | { [key: string]: Conversation[] }>(
|
38
53
|
initialConversations
|
39
54
|
);
|
55
|
+
const [isLoading, setIsLoading] = React.useState(false);
|
56
|
+
const [hasError, setHasError] = React.useState(false);
|
40
57
|
const displayMode = ChatbotDisplayMode.embedded;
|
41
58
|
|
42
59
|
const findMatchingItems = (targetValue: string) => {
|
@@ -74,6 +91,20 @@ export const ChatbotHeaderTitleDemo: React.FunctionComponent = () => {
|
|
74
91
|
id="drawer-actions-visible"
|
75
92
|
name="drawer-actions-visible"
|
76
93
|
></Checkbox>
|
94
|
+
<Checkbox
|
95
|
+
label="Show loading state"
|
96
|
+
isChecked={isLoading}
|
97
|
+
onChange={() => setIsLoading(!isLoading)}
|
98
|
+
id="drawer-is-loading"
|
99
|
+
name="drawer-is-loading"
|
100
|
+
></Checkbox>
|
101
|
+
<Checkbox
|
102
|
+
label="Show error state"
|
103
|
+
isChecked={hasError}
|
104
|
+
onChange={() => setHasError(!hasError)}
|
105
|
+
id="drawer-has-error"
|
106
|
+
name="drawer-has-error"
|
107
|
+
></Checkbox>
|
77
108
|
<ChatbotConversationHistoryNav
|
78
109
|
displayMode={displayMode}
|
79
110
|
onDrawerToggle={() => setIsOpen(!isOpen)}
|
@@ -96,6 +127,8 @@ export const ChatbotHeaderTitleDemo: React.FunctionComponent = () => {
|
|
96
127
|
setConversations(newConversations);
|
97
128
|
}}
|
98
129
|
drawerContent={<div>Drawer content</div>}
|
130
|
+
isLoading={isLoading}
|
131
|
+
errorState={hasError ? ERROR : undefined}
|
99
132
|
/>
|
100
133
|
</>
|
101
134
|
);
|
package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotWelcomeInteraction.tsx
CHANGED
@@ -116,7 +116,7 @@ export const ChatbotWelcomeInteractionDemo: React.FunctionComponent = () => {
|
|
116
116
|
so that users of assistive devices receive sufficient context */}
|
117
117
|
<MessageBox announcement={announcement} position={position}>
|
118
118
|
{messages.length === 0 && (
|
119
|
-
<ChatbotWelcomePrompt title="
|
119
|
+
<ChatbotWelcomePrompt title="Hi, ChatBot User!" description="How can I help you today?" />
|
120
120
|
)}
|
121
121
|
{/* This code block enables scrolling to the top of the last message.
|
122
122
|
You can instead choose to move the div with scrollToBottomRef on it below
|
@@ -1,19 +1,19 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
|
3
3
|
import ChatbotWelcomePrompt from '@patternfly/chatbot/dist/dynamic/ChatbotWelcomePrompt';
|
4
|
-
import Checkbox from '@patternfly/react-core';
|
4
|
+
import { Checkbox } from '@patternfly/react-core';
|
5
5
|
|
6
6
|
export const ChatbotWelcomePromptExample: React.FunctionComponent = () => {
|
7
7
|
const [showWelcomePrompts, setShowWelcomePrompts] = React.useState(true);
|
8
8
|
|
9
9
|
const welcomePrompts = [
|
10
10
|
{
|
11
|
-
title: '
|
12
|
-
message: '
|
11
|
+
title: 'Set up account',
|
12
|
+
message: 'Choose the necessary settings and preferences for your account.'
|
13
13
|
},
|
14
14
|
{
|
15
|
-
title: '
|
16
|
-
message: '
|
15
|
+
title: 'Troubleshoot issue',
|
16
|
+
message: 'Find documentation and instructions to resolve your issue.'
|
17
17
|
}
|
18
18
|
];
|
19
19
|
|
@@ -29,8 +29,8 @@ export const ChatbotWelcomePromptExample: React.FunctionComponent = () => {
|
|
29
29
|
id="showWelcomePrompts"
|
30
30
|
/>
|
31
31
|
<ChatbotWelcomePrompt
|
32
|
-
title="
|
33
|
-
description="How
|
32
|
+
title="Hi, ChatBot User!"
|
33
|
+
description="How can I help you today?"
|
34
34
|
{...(showWelcomePrompts && { prompts: welcomePrompts })}
|
35
35
|
/>
|
36
36
|
</>
|
@@ -36,20 +36,20 @@ import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
|
|
36
36
|
const footnoteProps = {
|
37
37
|
label: 'ChatBot uses AI. Check for mistakes.',
|
38
38
|
popover: {
|
39
|
-
title: 'Verify
|
40
|
-
description: `While ChatBot strives for accuracy,
|
39
|
+
title: 'Verify information',
|
40
|
+
description: `While ChatBot strives for accuracy, AI is experimental and can make mistakes. We cannot guarantee that all information provided by ChatBot is up to date or without error. You should always verify responses using reliable sources, especially for crucial information and decision making.`,
|
41
41
|
bannerImage: {
|
42
42
|
src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',
|
43
43
|
alt: 'Example image for footnote popover'
|
44
44
|
},
|
45
45
|
cta: {
|
46
|
-
label: '
|
46
|
+
label: 'Dismiss',
|
47
47
|
onClick: () => {
|
48
48
|
alert('Do something!');
|
49
49
|
}
|
50
50
|
},
|
51
51
|
link: {
|
52
|
-
label: '
|
52
|
+
label: 'View AI policy',
|
53
53
|
url: 'https://www.redhat.com/'
|
54
54
|
}
|
55
55
|
}
|
@@ -129,12 +129,12 @@ const initialMessages: MessageProps[] = [
|
|
129
129
|
|
130
130
|
const welcomePrompts = [
|
131
131
|
{
|
132
|
-
title: '
|
133
|
-
message: '
|
132
|
+
title: 'Set up account',
|
133
|
+
message: 'Choose the necessary settings and preferences for your account.'
|
134
134
|
},
|
135
135
|
{
|
136
|
-
title: '
|
137
|
-
message: '
|
136
|
+
title: 'Troubleshoot issue',
|
137
|
+
message: 'Find documentation and instructions to resolve your issue.'
|
138
138
|
}
|
139
139
|
];
|
140
140
|
|
@@ -436,8 +436,8 @@ export const ChatbotDemo: React.FunctionComponent = () => {
|
|
436
436
|
so that users of assistive devices receive sufficient context */}
|
437
437
|
<MessageBox announcement={announcement}>
|
438
438
|
<ChatbotWelcomePrompt
|
439
|
-
title="
|
440
|
-
description="How
|
439
|
+
title="Hi, ChatBot User!"
|
440
|
+
description="How can I help you today?"
|
441
441
|
prompts={welcomePrompts}
|
442
442
|
/>
|
443
443
|
{/* This code block enables scrolling to the top of the last message.
|
@@ -231,7 +231,7 @@ export const BasicDemo: React.FunctionComponent = () => {
|
|
231
231
|
{error}
|
232
232
|
</ChatbotAlert>
|
233
233
|
)}
|
234
|
-
<ChatbotWelcomePrompt title="
|
234
|
+
<ChatbotWelcomePrompt title="Hi, ChatBot User!" description="How can I help you today?" />
|
235
235
|
{messages.map((message) => (
|
236
236
|
<Message key={message.name} {...message} />
|
237
237
|
))}
|
@@ -232,7 +232,7 @@ export const AttachmentMenuDemo: React.FunctionComponent = () => {
|
|
232
232
|
{error}
|
233
233
|
</ChatbotAlert>
|
234
234
|
)}
|
235
|
-
<ChatbotWelcomePrompt title="
|
235
|
+
<ChatbotWelcomePrompt title="Hi, ChatBot User!" description="How can I help you today?" />
|
236
236
|
{messages.map((message) => (
|
237
237
|
<Message key={message.name} {...message} />
|
238
238
|
))}
|
@@ -44,20 +44,20 @@ import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
|
|
44
44
|
const footnoteProps = {
|
45
45
|
label: 'ChatBot uses AI. Check for mistakes.',
|
46
46
|
popover: {
|
47
|
-
title: 'Verify
|
48
|
-
description: `While ChatBot strives for accuracy,
|
47
|
+
title: 'Verify information',
|
48
|
+
description: `While ChatBot strives for accuracy, AI is experimental and can make mistakes. We cannot guarantee that all information provided by ChatBot is up to date or without error. You should always verify responses using reliable sources, especially for crucial information and decision making.`,
|
49
49
|
bannerImage: {
|
50
50
|
src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif',
|
51
51
|
alt: 'Example image for footnote popover'
|
52
52
|
},
|
53
53
|
cta: {
|
54
|
-
label: '
|
54
|
+
label: 'Dismiss',
|
55
55
|
onClick: () => {
|
56
56
|
alert('Do something!');
|
57
57
|
}
|
58
58
|
},
|
59
59
|
link: {
|
60
|
-
label: '
|
60
|
+
label: 'View AI policy',
|
61
61
|
url: 'https://www.redhat.com/'
|
62
62
|
}
|
63
63
|
}
|
@@ -137,12 +137,12 @@ const initialMessages: MessageProps[] = [
|
|
137
137
|
|
138
138
|
const welcomePrompts = [
|
139
139
|
{
|
140
|
-
title: '
|
141
|
-
message: '
|
140
|
+
title: 'Set up account',
|
141
|
+
message: 'Choose the necessary settings and preferences for your account.'
|
142
142
|
},
|
143
143
|
{
|
144
|
-
title: '
|
145
|
-
message: '
|
144
|
+
title: 'Troubleshoot issue',
|
145
|
+
message: 'Find documentation and instructions to resolve your issue.'
|
146
146
|
}
|
147
147
|
];
|
148
148
|
|
@@ -400,8 +400,8 @@ export const EmbeddedChatbotDemo: React.FunctionComponent = () => {
|
|
400
400
|
so that users of assistive devices receive sufficient context */}
|
401
401
|
<MessageBox announcement={announcement}>
|
402
402
|
<ChatbotWelcomePrompt
|
403
|
-
title="
|
404
|
-
description="How
|
403
|
+
title="Hi, ChatBot User!"
|
404
|
+
description="How can I help you today?"
|
405
405
|
prompts={welcomePrompts}
|
406
406
|
/>
|
407
407
|
{/* This code block enables scrolling to the top of the last message.
|
package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx
CHANGED
@@ -117,7 +117,7 @@ export const CompareChild = ({ name, input, hasNewInput, setIsSendButtonDisabled
|
|
117
117
|
</ChatbotHeader>
|
118
118
|
<ChatbotContent>
|
119
119
|
<MessageBox ariaLabel={`Scrollable message log for ${name}`} announcement={announcement}>
|
120
|
-
<ChatbotWelcomePrompt title="
|
120
|
+
<ChatbotWelcomePrompt title="Hi, ChatBot User!" description="How can I help you today?" />
|
121
121
|
{messages.map((message) => (
|
122
122
|
<Message key={message.id} {...message} />
|
123
123
|
))}
|
@@ -9,11 +9,11 @@ import { MenuToggleElement, Tooltip, MenuToggle, Dropdown, DropdownProps } from
|
|
9
9
|
import EllipsisIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
|
10
10
|
|
11
11
|
export interface ChatbotConversationHistoryDropdownProps extends Omit<DropdownProps, 'toggle'> {
|
12
|
-
/** Dropdown items rendered in conversation
|
12
|
+
/** Dropdown items rendered in conversation settings dropdown */
|
13
13
|
menuItems: React.ReactNode;
|
14
|
-
/** Optional classname applied to conversation
|
14
|
+
/** Optional classname applied to conversation settings dropdown */
|
15
15
|
menuClassName?: string;
|
16
|
-
/** Tooltip content and aria-label applied to conversation
|
16
|
+
/** Tooltip content and aria-label applied to conversation settings dropdown */
|
17
17
|
label?: string;
|
18
18
|
/** Callback for when user selects item. */
|
19
19
|
onSelect?: (event?: React.MouseEvent, value?: string | number) => void;
|
@@ -189,3 +189,17 @@
|
|
189
189
|
}
|
190
190
|
}
|
191
191
|
}
|
192
|
+
|
193
|
+
.pf-chatbot__history-loading {
|
194
|
+
display: flex;
|
195
|
+
padding: var(--pf-t--global--spacer--lg);
|
196
|
+
flex-direction: column;
|
197
|
+
gap: var(--pf-t--global--spacer--lg);
|
198
|
+
}
|
199
|
+
|
200
|
+
.pf-chatbot__history-loading-block {
|
201
|
+
display: flex;
|
202
|
+
flex-direction: column;
|
203
|
+
gap: var(--pf-t--global--spacer--sm);
|
204
|
+
align-self: stretch;
|
205
|
+
}
|
@@ -4,6 +4,37 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
4
4
|
|
5
5
|
import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
|
6
6
|
import ChatbotConversationHistoryNav, { Conversation } from './ChatbotConversationHistoryNav';
|
7
|
+
import { EmptyStateStatus, Spinner } from '@patternfly/react-core';
|
8
|
+
|
9
|
+
const ERROR = {
|
10
|
+
bodyText: (
|
11
|
+
<>
|
12
|
+
To try again, check your connection and reload this page. If the issue persists,{' '}
|
13
|
+
<a href="">contact the support team</a>.
|
14
|
+
</>
|
15
|
+
),
|
16
|
+
buttonText: 'Reload',
|
17
|
+
buttonIcon: <Spinner size="sm" />,
|
18
|
+
hasButton: true,
|
19
|
+
titleText: 'Could not load chat history',
|
20
|
+
status: EmptyStateStatus.danger,
|
21
|
+
onClick: () => alert('Clicked Reload')
|
22
|
+
};
|
23
|
+
|
24
|
+
const ERROR_WITHOUT_BUTTON = {
|
25
|
+
bodyText: (
|
26
|
+
<>
|
27
|
+
To try again, check your connection and reload this page. If the issue persists,{' '}
|
28
|
+
<a href="">contact the support team</a>.
|
29
|
+
</>
|
30
|
+
),
|
31
|
+
buttonText: 'Reload',
|
32
|
+
buttonIcon: <Spinner size="sm" />,
|
33
|
+
hasButton: false,
|
34
|
+
titleText: 'Could not load chat history',
|
35
|
+
status: EmptyStateStatus.danger,
|
36
|
+
onClick: () => alert('Clicked Reload')
|
37
|
+
};
|
7
38
|
|
8
39
|
describe('ChatbotConversationHistoryNav', () => {
|
9
40
|
const onDrawerToggle = jest.fn();
|
@@ -232,4 +263,103 @@ describe('ChatbotConversationHistoryNav', () => {
|
|
232
263
|
const element = container.querySelector('.test');
|
233
264
|
expect(element).toBeInTheDocument();
|
234
265
|
});
|
266
|
+
|
267
|
+
it('should show loading state if triggered', () => {
|
268
|
+
render(
|
269
|
+
<ChatbotConversationHistoryNav
|
270
|
+
onDrawerToggle={onDrawerToggle}
|
271
|
+
isDrawerOpen={true}
|
272
|
+
displayMode={ChatbotDisplayMode.fullscreen}
|
273
|
+
setIsDrawerOpen={jest.fn()}
|
274
|
+
reverseButtonOrder={false}
|
275
|
+
handleTextInputChange={jest.fn()}
|
276
|
+
conversations={initialConversations}
|
277
|
+
isLoading
|
278
|
+
/>
|
279
|
+
);
|
280
|
+
expect(screen.getByRole('dialog', { name: /Loading chatbot conversation history/i })).toBeTruthy();
|
281
|
+
expect(screen.getByRole('button', { name: /Close drawer panel/i })).toBeTruthy();
|
282
|
+
});
|
283
|
+
|
284
|
+
it('should pass alternative aria label to loading state', () => {
|
285
|
+
render(
|
286
|
+
<ChatbotConversationHistoryNav
|
287
|
+
onDrawerToggle={onDrawerToggle}
|
288
|
+
isDrawerOpen={true}
|
289
|
+
displayMode={ChatbotDisplayMode.fullscreen}
|
290
|
+
setIsDrawerOpen={jest.fn()}
|
291
|
+
reverseButtonOrder={false}
|
292
|
+
handleTextInputChange={jest.fn()}
|
293
|
+
conversations={initialConversations}
|
294
|
+
isLoading
|
295
|
+
loadingState={{ screenreaderText: 'I am a test' }}
|
296
|
+
/>
|
297
|
+
);
|
298
|
+
expect(screen.getByRole('dialog', { name: /I am a test/i })).toBeTruthy();
|
299
|
+
});
|
300
|
+
|
301
|
+
it('should accept errorState', () => {
|
302
|
+
render(
|
303
|
+
<ChatbotConversationHistoryNav
|
304
|
+
onDrawerToggle={onDrawerToggle}
|
305
|
+
isDrawerOpen={true}
|
306
|
+
displayMode={ChatbotDisplayMode.fullscreen}
|
307
|
+
setIsDrawerOpen={jest.fn()}
|
308
|
+
reverseButtonOrder={false}
|
309
|
+
handleTextInputChange={jest.fn()}
|
310
|
+
conversations={initialConversations}
|
311
|
+
errorState={ERROR}
|
312
|
+
/>
|
313
|
+
);
|
314
|
+
expect(
|
315
|
+
screen.getByRole('dialog', {
|
316
|
+
name: /Could not load chat history To try again, check your connection and reload this page. If the issue persists, contact the support team . Loading... Reload/i
|
317
|
+
})
|
318
|
+
).toBeTruthy();
|
319
|
+
expect(screen.getByRole('button', { name: /Close drawer panel/i })).toBeTruthy();
|
320
|
+
expect(screen.getByRole('button', { name: /Loading... Reload/i })).toBeTruthy();
|
321
|
+
expect(screen.getByRole('textbox', { name: /Filter menu items/i })).toBeTruthy();
|
322
|
+
expect(screen.getByRole('heading', { name: /Could not load chat history/i })).toBeTruthy();
|
323
|
+
});
|
324
|
+
|
325
|
+
it('should accept errorState without button', () => {
|
326
|
+
render(
|
327
|
+
<ChatbotConversationHistoryNav
|
328
|
+
onDrawerToggle={onDrawerToggle}
|
329
|
+
isDrawerOpen={true}
|
330
|
+
displayMode={ChatbotDisplayMode.fullscreen}
|
331
|
+
setIsDrawerOpen={jest.fn()}
|
332
|
+
reverseButtonOrder={false}
|
333
|
+
handleTextInputChange={jest.fn()}
|
334
|
+
conversations={initialConversations}
|
335
|
+
errorState={ERROR_WITHOUT_BUTTON}
|
336
|
+
/>
|
337
|
+
);
|
338
|
+
expect(
|
339
|
+
screen.getByRole('dialog', {
|
340
|
+
name: /Could not load chat history To try again, check your connection and reload this page. If the issue persists, contact the support team ./i
|
341
|
+
})
|
342
|
+
).toBeTruthy();
|
343
|
+
expect(screen.getByRole('button', { name: /Close drawer panel/i })).toBeTruthy();
|
344
|
+
expect(screen.queryByRole('button', { name: /Loading... Reload/i })).toBeFalsy();
|
345
|
+
expect(screen.getByRole('textbox', { name: /Filter menu items/i })).toBeTruthy();
|
346
|
+
expect(screen.getByRole('heading', { name: /Could not load chat history/i })).toBeTruthy();
|
347
|
+
});
|
348
|
+
|
349
|
+
it('should show loading state over error state if both are supplied', () => {
|
350
|
+
render(
|
351
|
+
<ChatbotConversationHistoryNav
|
352
|
+
onDrawerToggle={onDrawerToggle}
|
353
|
+
isDrawerOpen={true}
|
354
|
+
displayMode={ChatbotDisplayMode.fullscreen}
|
355
|
+
setIsDrawerOpen={jest.fn()}
|
356
|
+
reverseButtonOrder={false}
|
357
|
+
handleTextInputChange={jest.fn()}
|
358
|
+
conversations={initialConversations}
|
359
|
+
isLoading
|
360
|
+
errorState={ERROR}
|
361
|
+
/>
|
362
|
+
);
|
363
|
+
expect(screen.getByRole('dialog', { name: /Loading/i })).toBeTruthy();
|
364
|
+
});
|
235
365
|
});
|
@@ -29,12 +29,15 @@ import {
|
|
29
29
|
DrawerHeadProps,
|
30
30
|
DrawerActionsProps,
|
31
31
|
DrawerCloseButtonProps,
|
32
|
-
DrawerPanelBodyProps
|
32
|
+
DrawerPanelBodyProps,
|
33
|
+
SkeletonProps
|
33
34
|
} from '@patternfly/react-core';
|
34
35
|
|
35
36
|
import { OutlinedCommentAltIcon } from '@patternfly/react-icons';
|
36
37
|
import { ChatbotDisplayMode } from '../Chatbot/Chatbot';
|
37
38
|
import ConversationHistoryDropdown from './ChatbotConversationHistoryDropdown';
|
39
|
+
import LoadingState from './LoadingState';
|
40
|
+
import HistoryEmptyState, { HistoryEmptyStateProps } from './EmptyState';
|
38
41
|
|
39
42
|
export interface Conversation {
|
40
43
|
/** Conversation id */
|
@@ -45,11 +48,11 @@ export interface Conversation {
|
|
45
48
|
noIcon?: boolean;
|
46
49
|
/** Conversation */
|
47
50
|
text: string;
|
48
|
-
/** Dropdown items rendered in conversation
|
51
|
+
/** Dropdown items rendered in conversation settings dropdown */
|
49
52
|
menuItems?: React.ReactNode;
|
50
|
-
/** Optional classname applied to conversation
|
53
|
+
/** Optional classname applied to conversation settings dropdown */
|
51
54
|
menuClassName?: string;
|
52
|
-
/** Tooltip content and aria-label applied to conversation
|
55
|
+
/** Tooltip content and aria-label applied to conversation settings dropdown */
|
53
56
|
label?: string;
|
54
57
|
/** Callback for when user selects item. */
|
55
58
|
onSelect?: (event?: React.MouseEvent, value?: string | number) => void;
|
@@ -103,6 +106,12 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
|
|
103
106
|
drawerCloseButtonProps?: DrawerCloseButtonProps;
|
104
107
|
/** Additional props appleid to drawer panel body */
|
105
108
|
drawerPanelBodyProps?: DrawerPanelBodyProps;
|
109
|
+
/** Whether to show drawer loading state */
|
110
|
+
isLoading?: boolean;
|
111
|
+
/** Additional props for loading state */
|
112
|
+
loadingState?: SkeletonProps;
|
113
|
+
/** Content to show in error state. Error state will appear once content is passed in. */
|
114
|
+
errorState?: HistoryEmptyStateProps;
|
106
115
|
}
|
107
116
|
|
108
117
|
export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConversationHistoryNavProps> = ({
|
@@ -115,7 +124,7 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
|
|
115
124
|
newChatButtonText = 'New chat',
|
116
125
|
drawerContent,
|
117
126
|
onNewChat,
|
118
|
-
searchInputPlaceholder = 'Search...',
|
127
|
+
searchInputPlaceholder = 'Search previous conversations...',
|
119
128
|
searchInputAriaLabel = 'Filter menu items',
|
120
129
|
handleTextInputChange,
|
121
130
|
displayMode,
|
@@ -129,6 +138,9 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
|
|
129
138
|
drawerActionsProps,
|
130
139
|
drawerCloseButtonProps,
|
131
140
|
drawerPanelBodyProps,
|
141
|
+
isLoading,
|
142
|
+
loadingState,
|
143
|
+
errorState,
|
132
144
|
...props
|
133
145
|
}: ChatbotConversationHistoryNavProps) => {
|
134
146
|
const drawerRef = React.useRef<HTMLDivElement>(null);
|
@@ -194,24 +206,19 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
|
|
194
206
|
// Menu Content
|
195
207
|
// - Consumers should pass an array to <Chatbot> of the list of conversations
|
196
208
|
// - Groups could be optional, but items need to be ordered by date
|
197
|
-
const
|
198
|
-
|
199
|
-
<
|
200
|
-
|
201
|
-
|
209
|
+
const renderMenuContent = () => {
|
210
|
+
if (errorState) {
|
211
|
+
return <HistoryEmptyState {...errorState} />;
|
212
|
+
}
|
213
|
+
return (
|
214
|
+
<Menu isPlain onSelect={onSelectActiveItem} activeItemId={activeItemId} {...menuProps}>
|
215
|
+
<MenuContent>{buildMenu()}</MenuContent>
|
216
|
+
</Menu>
|
217
|
+
);
|
218
|
+
};
|
202
219
|
|
203
|
-
const
|
204
|
-
|
205
|
-
<DrawerHead {...drawerHeadProps}>
|
206
|
-
<DrawerActions
|
207
|
-
data-testid={drawerActionsTestId}
|
208
|
-
className={reverseButtonOrder ? 'pf-v6-c-drawer__actions--reversed' : ''}
|
209
|
-
{...drawerActionsProps}
|
210
|
-
>
|
211
|
-
<DrawerCloseButton onClick={onDrawerToggle} {...drawerCloseButtonProps} />
|
212
|
-
{onNewChat && <Button onClick={onNewChat}>{newChatButtonText}</Button>}
|
213
|
-
</DrawerActions>
|
214
|
-
</DrawerHead>
|
220
|
+
const renderDrawerContent = () => (
|
221
|
+
<>
|
215
222
|
{handleTextInputChange && (
|
216
223
|
<div className="pf-chatbot__input">
|
217
224
|
<SearchInput
|
@@ -221,10 +228,38 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
|
|
221
228
|
/>
|
222
229
|
</div>
|
223
230
|
)}
|
224
|
-
<DrawerPanelBody {...drawerPanelBodyProps}>{
|
225
|
-
|
231
|
+
<DrawerPanelBody {...drawerPanelBodyProps}>{renderMenuContent()}</DrawerPanelBody>
|
232
|
+
</>
|
226
233
|
);
|
227
234
|
|
235
|
+
const renderPanelContent = () => {
|
236
|
+
const drawer = (
|
237
|
+
<>
|
238
|
+
<DrawerHead {...drawerHeadProps}>
|
239
|
+
<DrawerActions
|
240
|
+
data-testid={drawerActionsTestId}
|
241
|
+
className={reverseButtonOrder ? 'pf-v6-c-drawer__actions--reversed' : ''}
|
242
|
+
{...drawerActionsProps}
|
243
|
+
>
|
244
|
+
<DrawerCloseButton onClick={onDrawerToggle} {...drawerCloseButtonProps} />
|
245
|
+
{onNewChat && <Button onClick={onNewChat}>{newChatButtonText}</Button>}
|
246
|
+
</DrawerActions>
|
247
|
+
</DrawerHead>
|
248
|
+
{isLoading ? <LoadingState {...loadingState} /> : renderDrawerContent()}
|
249
|
+
</>
|
250
|
+
);
|
251
|
+
return (
|
252
|
+
<DrawerPanelContent
|
253
|
+
aria-live="polite"
|
254
|
+
focusTrap={{ enabled: true }}
|
255
|
+
defaultSize="384px"
|
256
|
+
{...drawerPanelContentProps}
|
257
|
+
>
|
258
|
+
{drawer}
|
259
|
+
</DrawerPanelContent>
|
260
|
+
);
|
261
|
+
};
|
262
|
+
|
228
263
|
// An onKeyDown property must be passed to the Drawer component to handle closing
|
229
264
|
// the drawer panel and deactivating the focus trap via the Escape key.
|
230
265
|
const onEscape = (event: React.KeyboardEvent) => {
|
@@ -246,7 +281,7 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
|
|
246
281
|
isInline={displayMode === ChatbotDisplayMode.fullscreen || displayMode === ChatbotDisplayMode.embedded}
|
247
282
|
{...props}
|
248
283
|
>
|
249
|
-
<DrawerContent panelContent={
|
284
|
+
<DrawerContent panelContent={renderPanelContent()} {...drawerContentProps}>
|
250
285
|
<DrawerContentBody {...drawerContentBodyProps}>
|
251
286
|
<>
|
252
287
|
<div
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import {
|
2
|
+
Button,
|
3
|
+
EmptyState,
|
4
|
+
EmptyStateActions,
|
5
|
+
EmptyStateBody,
|
6
|
+
EmptyStateFooter,
|
7
|
+
EmptyStateProps
|
8
|
+
} from '@patternfly/react-core';
|
9
|
+
import React from 'react';
|
10
|
+
|
11
|
+
export interface HistoryEmptyStateProps extends EmptyStateProps {
|
12
|
+
onClick?: () => void;
|
13
|
+
bodyText?: string | React.ReactNode;
|
14
|
+
buttonText?: string;
|
15
|
+
buttonIcon?: React.ReactNode;
|
16
|
+
hasButton?: boolean;
|
17
|
+
}
|
18
|
+
|
19
|
+
export const HistoryEmptyState: React.FunctionComponent<HistoryEmptyStateProps> = ({
|
20
|
+
bodyText,
|
21
|
+
buttonIcon,
|
22
|
+
buttonText,
|
23
|
+
status,
|
24
|
+
titleText,
|
25
|
+
headingLevel,
|
26
|
+
onClick,
|
27
|
+
hasButton = false,
|
28
|
+
...props
|
29
|
+
}: HistoryEmptyStateProps) => (
|
30
|
+
<EmptyState status={status} titleText={titleText} headingLevel={headingLevel} {...props}>
|
31
|
+
<EmptyStateBody>{bodyText}</EmptyStateBody>
|
32
|
+
{hasButton && (
|
33
|
+
<EmptyStateFooter>
|
34
|
+
<EmptyStateActions>
|
35
|
+
<Button icon={buttonIcon} variant="secondary" onClick={onClick}>
|
36
|
+
{buttonText}
|
37
|
+
</Button>
|
38
|
+
</EmptyStateActions>
|
39
|
+
</EmptyStateFooter>
|
40
|
+
)}
|
41
|
+
</EmptyState>
|
42
|
+
);
|
43
|
+
|
44
|
+
export default HistoryEmptyState;
|