@patternfly/chatbot 6.9.0-prerelease.1 → 6.9.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/Chatbot/Chatbot.d.ts +2 -0
- package/dist/cjs/Chatbot/Chatbot.js +2 -2
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +1 -1
- package/dist/cjs/ChatbotFooter/ChatbotFootnote.js +1 -1
- package/dist/cjs/ChatbotModal/ChatbotModal.js +1 -1
- package/dist/cjs/ChatbotWelcomePrompt/ChatbotWelcomePrompt.js +1 -1
- package/dist/cjs/MessageBar/MessageBar.d.ts +1 -1
- package/dist/cjs/MessageBar/MessageBar.js +4 -3
- package/dist/cjs/MessageBar/SendButton.js +2 -2
- package/dist/cjs/SourcesCard/SourcesCard.d.ts +2 -0
- package/dist/cjs/SourcesCardBase/SourcesCardBase.d.ts +2 -0
- package/dist/cjs/SourcesCardBase/SourcesCardBase.js +2 -2
- package/dist/css/main.css +199 -61
- package/dist/css/main.css.map +1 -1
- package/dist/esm/Chatbot/Chatbot.d.ts +2 -0
- package/dist/esm/Chatbot/Chatbot.js +3 -3
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +1 -1
- package/dist/esm/ChatbotFooter/ChatbotFootnote.js +1 -1
- package/dist/esm/ChatbotModal/ChatbotModal.js +1 -1
- package/dist/esm/ChatbotWelcomePrompt/ChatbotWelcomePrompt.js +1 -1
- package/dist/esm/MessageBar/MessageBar.d.ts +1 -1
- package/dist/esm/MessageBar/MessageBar.js +4 -3
- package/dist/esm/MessageBar/SendButton.js +2 -2
- package/dist/esm/SourcesCard/SourcesCard.d.ts +2 -0
- package/dist/esm/SourcesCardBase/SourcesCardBase.d.ts +2 -0
- package/dist/esm/SourcesCardBase/SourcesCardBase.js +2 -2
- package/package.json +9 -9
- package/patternfly-docs/content/extensions/chatbot/chatbot.md +1 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +31 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +2 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Canvas.md +132 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Canvas.tsx +615 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/CanvasWithDockedNav.tsx +697 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +39 -5
- package/patternfly-docs/content/extensions/chatbot/examples/demos/FullscreenDockedNav.tsx +385 -0
- package/patternfly-docs/patternfly-docs.config.js +1 -1
- package/src/Canvas/Canvas.scss +150 -0
- package/src/Chatbot/Chatbot.scss +13 -1
- package/src/Chatbot/Chatbot.test.tsx +11 -0
- package/src/Chatbot/Chatbot.tsx +5 -1
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +91 -77
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +32 -13
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +1 -0
- package/src/ChatbotFooter/ChatbotFooternote.test.tsx +50 -0
- package/src/ChatbotFooter/ChatbotFootnote.tsx +1 -1
- package/src/ChatbotModal/ChatbotModal.tsx +1 -0
- package/src/ChatbotToggle/ChatbotToggle.scss +3 -2
- package/src/ChatbotToggle/ChatbotToggle.test.tsx +4 -0
- package/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.scss +9 -0
- package/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx +4 -2
- package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +2 -6
- package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +2 -6
- package/src/Message/Message.scss +9 -0
- package/src/MessageBar/AttachButton.scss +4 -21
- package/src/MessageBar/MessageBar.scss +2 -2
- package/src/MessageBar/MessageBar.test.tsx +20 -0
- package/src/MessageBar/MessageBar.tsx +7 -3
- package/src/MessageBar/SendButton.scss +1 -32
- package/src/MessageBar/SendButton.tsx +2 -2
- package/src/SourcesCard/SourcesCard.scss +6 -0
- package/src/SourcesCard/SourcesCard.test.tsx +16 -0
- package/src/SourcesCard/SourcesCard.tsx +2 -0
- package/src/SourcesCardBase/SourcesCardBase.test.tsx +62 -0
- package/src/SourcesCardBase/SourcesCardBase.tsx +8 -0
- package/src/main.scss +1 -0
|
@@ -37,6 +37,7 @@ import MessageBox from '@patternfly/chatbot/dist/dynamic/MessageBox';
|
|
|
37
37
|
import Message from '@patternfly/chatbot/dist/dynamic/Message';
|
|
38
38
|
import Compare from '@patternfly/chatbot/dist/dynamic/Compare';
|
|
39
39
|
import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
|
|
40
|
+
import SettingsForm from '@patternfly/chatbot/dist/dynamic/Settings';
|
|
40
41
|
|
|
41
42
|
import ChatbotHeader, {
|
|
42
43
|
ChatbotHeaderMain,
|
|
@@ -48,11 +49,31 @@ ChatbotHeaderOptionsDropdown,
|
|
|
48
49
|
ChatbotHeaderCloseButton,
|
|
49
50
|
} from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
|
|
50
51
|
|
|
51
|
-
import { RhUiExpandIcon, RhUiOpenDrawerRightIcon, RhUiMenuBarsIcon, RhUiRestoreWindowIcon } from '@patternfly/react-icons';
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
52
|
+
import { RhUiExpandIcon, RhUiOpenDrawerRightIcon, RhUiMenuBarsIcon, RhUiRestoreWindowIcon, RhUiAiEditIcon } from '@patternfly/react-icons';
|
|
53
|
+
import { CodeEditor, Language } from '@patternfly/react-code-editor';
|
|
54
|
+
import { useDropzone } from 'react-dropzone';
|
|
55
|
+
import RhUiAddIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-add-icon';
|
|
56
|
+
import RhUiClipboardIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-clipboard-icon';
|
|
57
|
+
import RhUiCodeIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-code-icon';
|
|
58
|
+
import RhUiUploadIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-upload-icon';
|
|
59
|
+
import RhUiBuildFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-build-fill-icon';
|
|
60
|
+
import RhUiCopyFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-copy-fill-icon';
|
|
61
|
+
import RhUiImageFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-image-fill-icon';
|
|
62
|
+
import RhUiEditFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-edit-fill-icon';
|
|
63
|
+
import RhUiSettingsFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-settings-fill-icon';
|
|
64
|
+
import RhMicronsCloseIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-close-icon';
|
|
54
65
|
import {
|
|
55
66
|
Button,
|
|
67
|
+
Divider,
|
|
68
|
+
Drawer,
|
|
69
|
+
DrawerActions,
|
|
70
|
+
DrawerCloseButton,
|
|
71
|
+
DrawerContent,
|
|
72
|
+
DrawerContentBody,
|
|
73
|
+
DrawerHead,
|
|
74
|
+
DrawerPanelContent,
|
|
75
|
+
DropdownItem,
|
|
76
|
+
DropdownList,
|
|
56
77
|
DescriptionList,
|
|
57
78
|
DescriptionListDescription,
|
|
58
79
|
DescriptionListGroup,
|
|
@@ -61,7 +82,12 @@ ExpandableSection,
|
|
|
61
82
|
ExpandableSectionVariant,
|
|
62
83
|
Flex,
|
|
63
84
|
FlexItem,
|
|
64
|
-
Label
|
|
85
|
+
Label,
|
|
86
|
+
MenuToggle,
|
|
87
|
+
Select,
|
|
88
|
+
SelectList,
|
|
89
|
+
SelectOption,
|
|
90
|
+
Title
|
|
65
91
|
} from '@patternfly/react-core';
|
|
66
92
|
import PFHorizontalLogoColor from '../UI/PF-HorizontalLogo-Color.svg';
|
|
67
93
|
import PFHorizontalLogoReverse from '../UI/PF-HorizontalLogo-Reverse.svg';
|
|
@@ -70,7 +96,7 @@ import PFIconLogoReverse from '../UI/PF-IconLogo-Reverse.svg';
|
|
|
70
96
|
import userAvatar from '../Messages/user_avatar.svg';
|
|
71
97
|
import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
|
|
72
98
|
import { getTrackingProviders } from "@patternfly/chatbot/dist/dynamic/tracking";
|
|
73
|
-
import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent, MouseEvent as ReactMouseEvent } from 'react';
|
|
99
|
+
import { useEffect,useCallback, useRef, useState, FunctionComponent, MouseEvent, MouseEvent as ReactMouseEvent, ReactNode } from 'react';
|
|
74
100
|
import saveAs from 'file-saver';
|
|
75
101
|
|
|
76
102
|
### Basic ChatBot
|
|
@@ -166,6 +192,14 @@ This demo showcases how the ChatBot can be rendered in different display modes t
|
|
|
166
192
|
|
|
167
193
|
```
|
|
168
194
|
|
|
195
|
+
### Fullscreen ChatBot with docked navigation
|
|
196
|
+
|
|
197
|
+
This demo shows a headerless fullscreen ChatBot with a vertical navigation rail for switching between chat history, a new chat, and settings.
|
|
198
|
+
|
|
199
|
+
```js file="./FullscreenDockedNav.tsx" isFullscreen
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
|
|
169
203
|
### Comparing ChatBots
|
|
170
204
|
|
|
171
205
|
To let users compare how different ChatBots respond to the same prompt, you can add multiple ChatBots within the same window. The following demo illustrates a comparison view pattern that allows users to toggle between different conversations in a single ChatBot window.
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { FunctionComponent, ReactNode, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Avatar,
|
|
4
|
+
Brand,
|
|
5
|
+
Button,
|
|
6
|
+
Divider,
|
|
7
|
+
Dropdown,
|
|
8
|
+
DropdownItem,
|
|
9
|
+
DropdownList,
|
|
10
|
+
Masthead,
|
|
11
|
+
MastheadBrand,
|
|
12
|
+
MastheadContent,
|
|
13
|
+
MastheadLogo,
|
|
14
|
+
MastheadMain,
|
|
15
|
+
MastheadToggle,
|
|
16
|
+
Flex,
|
|
17
|
+
MenuToggle,
|
|
18
|
+
Nav,
|
|
19
|
+
NavItem,
|
|
20
|
+
NavList,
|
|
21
|
+
PageToggleButton,
|
|
22
|
+
Switch,
|
|
23
|
+
Title,
|
|
24
|
+
Tooltip
|
|
25
|
+
} from '@patternfly/react-core';
|
|
26
|
+
import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
|
|
27
|
+
import ChatbotContent from '@patternfly/chatbot/dist/dynamic/ChatbotContent';
|
|
28
|
+
import ChatbotWelcomePrompt from '@patternfly/chatbot/dist/dynamic/ChatbotWelcomePrompt';
|
|
29
|
+
import ChatbotFooter, { ChatbotFootnote } from '@patternfly/chatbot/dist/dynamic/ChatbotFooter';
|
|
30
|
+
import MessageBar from '@patternfly/chatbot/dist/dynamic/MessageBar';
|
|
31
|
+
import MessageBox from '@patternfly/chatbot/dist/dynamic/MessageBox';
|
|
32
|
+
import Message, { MessageProps } from '@patternfly/chatbot/dist/dynamic/Message';
|
|
33
|
+
import ChatbotConversationHistoryNav from '@patternfly/chatbot/dist/dynamic/ChatbotConversationHistoryNav';
|
|
34
|
+
import { RhUiEditFillIcon } from '@patternfly/react-icons/dist/esm/icons/rh-ui-edit-fill-icon';
|
|
35
|
+
import { RhUiSettingsFillIcon } from '@patternfly/react-icons/dist/esm/icons/rh-ui-settings-fill-icon';
|
|
36
|
+
import { RhMicronsCloseIcon } from '@patternfly/react-icons/dist/esm/icons/rh-microns-close-icon';
|
|
37
|
+
import PFIconLogoColor from '../UI/PF-IconLogo-Color.svg';
|
|
38
|
+
import userAvatar from '../Messages/user_avatar.svg';
|
|
39
|
+
import '@patternfly/react-core/dist/styles/base.css';
|
|
40
|
+
import '@patternfly/chatbot/dist/css/main.css';
|
|
41
|
+
|
|
42
|
+
const initialMessages: MessageProps[] = [
|
|
43
|
+
{
|
|
44
|
+
id: '1',
|
|
45
|
+
role: 'user',
|
|
46
|
+
content: 'Show me how a fullscreen chatbot can use docked navigation.',
|
|
47
|
+
name: 'You',
|
|
48
|
+
avatar: userAvatar,
|
|
49
|
+
timestamp: new Date().toLocaleString(),
|
|
50
|
+
avatarProps: { isBordered: true }
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: '2',
|
|
54
|
+
role: 'bot',
|
|
55
|
+
content: 'Use the rail on the left to switch between the conversation, history, and a new chat.',
|
|
56
|
+
name: 'Bot',
|
|
57
|
+
timestamp: new Date().toLocaleString()
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const welcomePrompts = [
|
|
62
|
+
{
|
|
63
|
+
title: 'Topic 1',
|
|
64
|
+
message: 'Helpful prompt for Topic 1'
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
title: 'Topic 2',
|
|
68
|
+
message: 'Helpful prompt for Topic 2'
|
|
69
|
+
}
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const conversations = [
|
|
73
|
+
{ id: '1', text: 'Fullscreen chatbot navigation' },
|
|
74
|
+
{ id: '2', text: 'Review deployment options' }
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
interface PlainTextSettingsFormProps {
|
|
78
|
+
fields: { id: string; label: string; field: ReactNode }[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const PlainTextSettingsForm = ({ fields }: PlainTextSettingsFormProps) => (
|
|
82
|
+
<div className="pf-chatbot__settings-form-container">
|
|
83
|
+
<form className="pf-chatbot__settings-form">
|
|
84
|
+
{fields.map(({ id, label, field }) => (
|
|
85
|
+
<div className="pf-chatbot__settings-form-row" key={id}>
|
|
86
|
+
<div className="pf-chatbot__settings-label" id={`${id}-label`}>
|
|
87
|
+
{label}
|
|
88
|
+
</div>
|
|
89
|
+
{field}
|
|
90
|
+
</div>
|
|
91
|
+
))}
|
|
92
|
+
</form>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const hamburgerHoverStyles = `
|
|
97
|
+
.pf-chatbot__canvas-docked-nav .pf-v6-c-masthead__logo.pf-m-compact {
|
|
98
|
+
display: revert;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.pf-chatbot__canvas-docked-nav .pf-v6-c-nav.pf-m-docked .pf-v6-c-nav__link-text {
|
|
102
|
+
display: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.pf-chatbot__canvas-history-toggle.pf-v6-c-button.pf-m-hamburger:is(:hover, :focus-visible) {
|
|
106
|
+
--pf-v6-c-button--hamburger-icon--top--path: var(--pf-v6-c-button--hamburger-icon--top--collapse--path);
|
|
107
|
+
--pf-v6-c-button--hamburger-icon--arrow--path: var(--pf-v6-c-button--hamburger-icon--arrow--collapse--path);
|
|
108
|
+
--pf-v6-c-button--hamburger-icon--bottom--path: var(--pf-v6-c-button--hamburger-icon--bottom--collapse--path);
|
|
109
|
+
--pf-v6-c-button__icon--TransitionDelay: 0s;
|
|
110
|
+
--pf-v6-c-button__icon--ScaleX: var(--pf-v6-c-button--m-hamburger__icon--m-expand--ScaleX);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.pf-chatbot__canvas-history-toggle.pf-v6-c-button.pf-m-hamburger[aria-expanded="true"]:is(:hover, :focus-visible) {
|
|
114
|
+
--pf-v6-c-button__icon--ScaleX: var(--pf-v6-c-button--m-hamburger__icon--m-collapse--ScaleX);
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
const SettingsPanel = ({ onClose }) => {
|
|
119
|
+
const [openDropdown, setOpenDropdown] = useState<string>();
|
|
120
|
+
const [theme, setTheme] = useState('System');
|
|
121
|
+
const [language, setLanguage] = useState('Auto-detect');
|
|
122
|
+
const [voice, setVoice] = useState('Bot');
|
|
123
|
+
const [isAnalyticsShared, setIsAnalyticsShared] = useState(true);
|
|
124
|
+
const closeButtonRef = useRef<HTMLButtonElement>(null);
|
|
125
|
+
|
|
126
|
+
useEffect(() => {
|
|
127
|
+
window.requestAnimationFrame(() => closeButtonRef.current?.focus());
|
|
128
|
+
}, []);
|
|
129
|
+
|
|
130
|
+
const dropdownField = (id, label, value, options, onSelect) => (
|
|
131
|
+
<Dropdown
|
|
132
|
+
isOpen={openDropdown === id}
|
|
133
|
+
onSelect={(_event, selectedValue) => {
|
|
134
|
+
onSelect(String(selectedValue));
|
|
135
|
+
setOpenDropdown(undefined);
|
|
136
|
+
}}
|
|
137
|
+
onOpenChange={(isOpen: boolean) => setOpenDropdown(isOpen ? id : undefined)}
|
|
138
|
+
shouldFocusToggleOnSelect
|
|
139
|
+
shouldFocusFirstItemOnOpen
|
|
140
|
+
toggle={(toggleRef) => (
|
|
141
|
+
<MenuToggle
|
|
142
|
+
id={id}
|
|
143
|
+
ref={toggleRef}
|
|
144
|
+
onClick={() => setOpenDropdown(openDropdown === id ? undefined : id)}
|
|
145
|
+
isExpanded={openDropdown === id}
|
|
146
|
+
aria-label={`${value}, ${label}`}
|
|
147
|
+
>
|
|
148
|
+
{value}
|
|
149
|
+
</MenuToggle>
|
|
150
|
+
)}
|
|
151
|
+
>
|
|
152
|
+
<DropdownList>
|
|
153
|
+
{options.map((option) => (
|
|
154
|
+
<DropdownItem value={option} key={option}>
|
|
155
|
+
{option}
|
|
156
|
+
</DropdownItem>
|
|
157
|
+
))}
|
|
158
|
+
</DropdownList>
|
|
159
|
+
</Dropdown>
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const fields = [
|
|
163
|
+
{
|
|
164
|
+
id: 'theme',
|
|
165
|
+
label: 'Theme',
|
|
166
|
+
field: dropdownField('theme', 'Theme', theme, ['System', 'Light', 'Dark'], setTheme)
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'language',
|
|
170
|
+
label: 'Language',
|
|
171
|
+
field: dropdownField('language', 'Language', language, ['Auto-detect', 'English'], setLanguage)
|
|
172
|
+
},
|
|
173
|
+
{ id: 'voice', label: 'Voice', field: dropdownField('voice', 'Voice', voice, ['Bot', 'User'], setVoice) },
|
|
174
|
+
{
|
|
175
|
+
id: 'analytics',
|
|
176
|
+
label: 'Share analytics',
|
|
177
|
+
field: (
|
|
178
|
+
<Switch
|
|
179
|
+
id="analytics"
|
|
180
|
+
aria-labelledby="analytics-label"
|
|
181
|
+
isChecked={isAnalyticsShared}
|
|
182
|
+
onChange={(_event, checked) => setIsAnalyticsShared(checked)}
|
|
183
|
+
/>
|
|
184
|
+
)
|
|
185
|
+
},
|
|
186
|
+
{ id: 'archived-chat', label: 'Archived chats', field: <Button id="archived-chat">Manage</Button> },
|
|
187
|
+
{ id: 'archive-all', label: 'Archived all chats', field: <Button id="archive-all">Archive all</Button> },
|
|
188
|
+
{
|
|
189
|
+
id: 'delete-all',
|
|
190
|
+
label: 'Delete all chats',
|
|
191
|
+
field: (
|
|
192
|
+
<Button id="delete-all" variant="danger">
|
|
193
|
+
Delete all
|
|
194
|
+
</Button>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<div className="pf-v6-u-w-100">
|
|
201
|
+
<div className="pf-v6-u-w-100 pf-v6-u-mx-auto" style={{ maxWidth: '60rem' }}>
|
|
202
|
+
<Flex
|
|
203
|
+
justifyContent={{ default: 'justifyContentSpaceBetween' }}
|
|
204
|
+
alignItems={{ default: 'alignItemsCenter' }}
|
|
205
|
+
className="pf-v6-u-p-lg"
|
|
206
|
+
>
|
|
207
|
+
<Title headingLevel="h1" size="2xl">
|
|
208
|
+
Settings
|
|
209
|
+
</Title>
|
|
210
|
+
<Tooltip triggerRef={closeButtonRef} content="Close settings" position="bottom" aria="none" />
|
|
211
|
+
<Button
|
|
212
|
+
ref={closeButtonRef}
|
|
213
|
+
variant="plain"
|
|
214
|
+
icon={<RhMicronsCloseIcon />}
|
|
215
|
+
aria-label="Close settings"
|
|
216
|
+
onClick={onClose}
|
|
217
|
+
/>
|
|
218
|
+
</Flex>
|
|
219
|
+
<Divider />
|
|
220
|
+
<PlainTextSettingsForm fields={fields} />
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
export const FullscreenDockedNav: FunctionComponent = () => {
|
|
227
|
+
const [messages, setMessages] = useState<MessageProps[]>(initialMessages);
|
|
228
|
+
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
229
|
+
const [areSettingsOpen, setAreSettingsOpen] = useState(false);
|
|
230
|
+
const [announcement, setAnnouncement] = useState<string>();
|
|
231
|
+
const newChatRef = useRef<HTMLAnchorElement>(null);
|
|
232
|
+
const settingsRef = useRef<HTMLAnchorElement>(null);
|
|
233
|
+
|
|
234
|
+
const closeSettings = () => {
|
|
235
|
+
setAreSettingsOpen(false);
|
|
236
|
+
window.requestAnimationFrame(() => settingsRef.current?.focus());
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const startNewChat = () => {
|
|
240
|
+
setMessages([]);
|
|
241
|
+
setIsDrawerOpen(false);
|
|
242
|
+
setAreSettingsOpen(false);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const sendMessage = (content: string) => {
|
|
246
|
+
const message: MessageProps = {
|
|
247
|
+
id: Date.now().toString(),
|
|
248
|
+
role: 'user',
|
|
249
|
+
content,
|
|
250
|
+
name: 'You',
|
|
251
|
+
avatar: userAvatar,
|
|
252
|
+
timestamp: new Date().toLocaleString(),
|
|
253
|
+
avatarProps: { isBordered: true }
|
|
254
|
+
};
|
|
255
|
+
setMessages((currentMessages) => [...currentMessages, message]);
|
|
256
|
+
setAnnouncement(`Message from You: ${content}`);
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const dockedNav = (
|
|
260
|
+
<>
|
|
261
|
+
<style>{hamburgerHoverStyles}</style>
|
|
262
|
+
<div className="pf-chatbot__canvas-docked-nav pf-v6-u-h-100 pf-v6-u-p-sm">
|
|
263
|
+
<Masthead variant="docked">
|
|
264
|
+
<MastheadMain>
|
|
265
|
+
<MastheadToggle>
|
|
266
|
+
<PageToggleButton
|
|
267
|
+
className="pf-chatbot__canvas-history-toggle"
|
|
268
|
+
aria-label="Chat history"
|
|
269
|
+
isHamburgerButton
|
|
270
|
+
isSidebarOpen={isDrawerOpen}
|
|
271
|
+
onSidebarToggle={() => setIsDrawerOpen((open) => !open)}
|
|
272
|
+
/>
|
|
273
|
+
</MastheadToggle>
|
|
274
|
+
<MastheadBrand>
|
|
275
|
+
<MastheadLogo isCompact>
|
|
276
|
+
<Brand src={PFIconLogoColor} alt="PatternFly" heights={{ default: '37px' }} />
|
|
277
|
+
</MastheadLogo>
|
|
278
|
+
</MastheadBrand>
|
|
279
|
+
</MastheadMain>
|
|
280
|
+
<MastheadContent>
|
|
281
|
+
<Divider />
|
|
282
|
+
<Nav
|
|
283
|
+
variant="docked"
|
|
284
|
+
aria-label="Chatbot navigation"
|
|
285
|
+
className="pf-v6-u-flex-1 pf-v6-u-align-content-space-between"
|
|
286
|
+
>
|
|
287
|
+
<NavList>
|
|
288
|
+
<NavItem
|
|
289
|
+
itemId="new-chat"
|
|
290
|
+
aria-label="New chat"
|
|
291
|
+
icon={<RhUiEditFillIcon />}
|
|
292
|
+
component="button"
|
|
293
|
+
preventDefault
|
|
294
|
+
anchorRef={newChatRef}
|
|
295
|
+
onClick={startNewChat}
|
|
296
|
+
>
|
|
297
|
+
New chat
|
|
298
|
+
</NavItem>
|
|
299
|
+
</NavList>
|
|
300
|
+
<NavList className="pf-v6-u-mt-auto">
|
|
301
|
+
<NavItem
|
|
302
|
+
itemId="settings"
|
|
303
|
+
aria-label="Settings"
|
|
304
|
+
icon={<RhUiSettingsFillIcon />}
|
|
305
|
+
component="button"
|
|
306
|
+
preventDefault
|
|
307
|
+
anchorRef={settingsRef}
|
|
308
|
+
onClick={() => {
|
|
309
|
+
setIsDrawerOpen(false);
|
|
310
|
+
setAreSettingsOpen(true);
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
Settings
|
|
314
|
+
</NavItem>
|
|
315
|
+
</NavList>
|
|
316
|
+
</Nav>
|
|
317
|
+
<Tooltip aria="none" aria-live="off" triggerRef={newChatRef} content="New chat" />
|
|
318
|
+
<Tooltip aria="none" aria-live="off" triggerRef={settingsRef} content="Settings" />
|
|
319
|
+
<Avatar
|
|
320
|
+
className="pf-v6-u-mt-md pf-v6-u-mb-md pf-v6-u-mx-auto"
|
|
321
|
+
src={userAvatar}
|
|
322
|
+
alt="User profile"
|
|
323
|
+
size="md"
|
|
324
|
+
isBordered
|
|
325
|
+
/>
|
|
326
|
+
</MastheadContent>
|
|
327
|
+
</Masthead>
|
|
328
|
+
</div>
|
|
329
|
+
</>
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
return (
|
|
333
|
+
<Chatbot displayMode={ChatbotDisplayMode.fullscreen} dockedNav={dockedNav}>
|
|
334
|
+
<div className="pf-chatbot__canvas">
|
|
335
|
+
<ChatbotConversationHistoryNav
|
|
336
|
+
displayMode={ChatbotDisplayMode.fullscreen}
|
|
337
|
+
isDrawerOpen={isDrawerOpen}
|
|
338
|
+
setIsDrawerOpen={setIsDrawerOpen}
|
|
339
|
+
onDrawerToggle={() => setIsDrawerOpen((open) => !open)}
|
|
340
|
+
conversations={{ Today: conversations }}
|
|
341
|
+
activeItemId="1"
|
|
342
|
+
onNewChat={startNewChat}
|
|
343
|
+
drawerCloseButtonProps={{ 'aria-label': 'Close chat history' }}
|
|
344
|
+
drawerContent={
|
|
345
|
+
<div className="pf-chatbot__canvas">
|
|
346
|
+
{areSettingsOpen ? (
|
|
347
|
+
<SettingsPanel onClose={closeSettings} />
|
|
348
|
+
) : (
|
|
349
|
+
<div className="pf-chatbot__canvas-column">
|
|
350
|
+
<ChatbotContent>
|
|
351
|
+
<MessageBox
|
|
352
|
+
ariaLabel="Scrollable message log for ChatBot"
|
|
353
|
+
announcement={announcement}
|
|
354
|
+
position="bottom"
|
|
355
|
+
>
|
|
356
|
+
<ChatbotWelcomePrompt
|
|
357
|
+
title="Hello, Chatbot User"
|
|
358
|
+
description="How may I help you today?"
|
|
359
|
+
prompts={welcomePrompts}
|
|
360
|
+
/>
|
|
361
|
+
{messages.map((message) => (
|
|
362
|
+
<Message key={message.id} {...message} />
|
|
363
|
+
))}
|
|
364
|
+
</MessageBox>
|
|
365
|
+
</ChatbotContent>
|
|
366
|
+
<ChatbotFooter>
|
|
367
|
+
<MessageBar onSendMessage={sendMessage} attachButtonPosition="start" alwayShowSendButton />
|
|
368
|
+
<ChatbotFootnote
|
|
369
|
+
label="Always review AI-generated content prior to use."
|
|
370
|
+
popover={{
|
|
371
|
+
title: 'AI-generated content',
|
|
372
|
+
description: 'Always review AI-generated content prior to use.',
|
|
373
|
+
showClose: true
|
|
374
|
+
}}
|
|
375
|
+
/>
|
|
376
|
+
</ChatbotFooter>
|
|
377
|
+
</div>
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
}
|
|
381
|
+
/>
|
|
382
|
+
</div>
|
|
383
|
+
</Chatbot>
|
|
384
|
+
);
|
|
385
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Canvas
|
|
3
|
+
// ============================================================================
|
|
4
|
+
.pf-chatbot__canvas {
|
|
5
|
+
--pf-chatbot__canvas--BackgroundColor: var(--pf-t--global--background--color--secondary--default);
|
|
6
|
+
--pf-chatbot__canvas--Padding: var(--pf-t--global--spacer--lg);
|
|
7
|
+
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
flex: 1;
|
|
11
|
+
min-height: 0;
|
|
12
|
+
height: 100%;
|
|
13
|
+
background-color: var(--pf-chatbot__canvas--BackgroundColor);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:where(.pf-v6-theme-glass) .pf-chatbot__canvas {
|
|
17
|
+
--pf-chatbot__canvas--BackgroundColor: var(--pf-t--global--background--color--glass--primary--default);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.pf-chatbot__canvas-docked-nav {
|
|
21
|
+
border-inline-end: var(--pf-t--global--border--width--regular) solid var(--pf-t--global--border--color--subtle);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Drawer
|
|
25
|
+
// ----------------------------------------------------------------------------
|
|
26
|
+
.pf-chatbot__canvas-drawer {
|
|
27
|
+
flex: 1;
|
|
28
|
+
min-height: 0;
|
|
29
|
+
height: 100%;
|
|
30
|
+
background-color: var(--pf-chatbot__canvas--BackgroundColor);
|
|
31
|
+
--pf-v6-c-drawer__panel--Opacity: 1;
|
|
32
|
+
--pf-v6-c-drawer__panel--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
33
|
+
--pf-v6-c-drawer__panel--m-inline--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
34
|
+
--pf-v6-c-drawer__panel--m-inline--md--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
35
|
+
--pf-v6-c-drawer__panel--m-glass--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
36
|
+
--pf-v6-c-code-editor__main--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
37
|
+
|
|
38
|
+
> .pf-v6-c-drawer__main,
|
|
39
|
+
> .pf-v6-c-drawer__main > .pf-v6-c-drawer__panel,
|
|
40
|
+
> .pf-v6-c-drawer__main > .pf-v6-c-drawer__panel .pf-v6-c-drawer__panel-main {
|
|
41
|
+
background-color: var(--pf-chatbot__canvas--BackgroundColor);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@media screen and (min-width: 48rem) {
|
|
46
|
+
.pf-v6-c-drawer.pf-m-inline.pf-chatbot__canvas-drawer {
|
|
47
|
+
--pf-v6-c-drawer__panel--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
48
|
+
--pf-v6-c-drawer__panel--m-inline--md--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.pf-chatbot__canvas-body {
|
|
53
|
+
height: 100%;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.pf-chatbot__canvas-column {
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
height: 100%;
|
|
60
|
+
min-height: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.pf-chatbot__canvas-chat-content {
|
|
64
|
+
padding-block-start: var(--pf-t--global--spacer--lg);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.pf-chatbot__canvas-chat-footer {
|
|
68
|
+
padding-block-end: var(--pf-t--global--spacer--md);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Panel
|
|
72
|
+
// ----------------------------------------------------------------------------
|
|
73
|
+
.pf-chatbot__canvas-section {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: column;
|
|
76
|
+
flex: 1;
|
|
77
|
+
min-height: 0;
|
|
78
|
+
height: 100%;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.pf-chatbot__canvas-panel {
|
|
82
|
+
height: 100%;
|
|
83
|
+
overflow: hidden;
|
|
84
|
+
background-color: var(--pf-chatbot__canvas--BackgroundColor);
|
|
85
|
+
--pf-v6-c-drawer__panel--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
86
|
+
--pf-v6-c-drawer__panel--m-inline--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
87
|
+
--pf-v6-c-drawer__panel--m-inline--md--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
88
|
+
--pf-v6-c-drawer__panel--m-glass--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
89
|
+
--pf-v6-c-drawer__panel--PaddingBlockStart: 0;
|
|
90
|
+
--pf-v6-c-drawer__panel--PaddingBlockEnd: 0;
|
|
91
|
+
--pf-v6-c-drawer__panel--RowGap: 0;
|
|
92
|
+
|
|
93
|
+
.pf-v6-c-drawer__panel-main {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
min-height: 0;
|
|
97
|
+
height: 100%;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.pf-chatbot__canvas-panel-body {
|
|
101
|
+
display: flex;
|
|
102
|
+
flex-direction: column;
|
|
103
|
+
flex: 1;
|
|
104
|
+
min-height: 0;
|
|
105
|
+
padding-block-end: var(--pf-chatbot__canvas--Padding);
|
|
106
|
+
padding-inline: var(--pf-chatbot__canvas--Padding);
|
|
107
|
+
box-sizing: border-box;
|
|
108
|
+
background-color: var(--pf-chatbot__canvas--BackgroundColor);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.pf-chatbot__canvas-head {
|
|
112
|
+
width: 100%;
|
|
113
|
+
grid-template-columns: minmax(0, 1fr) max-content;
|
|
114
|
+
--pf-v6-c-drawer__head--PaddingBlockStart: var(--pf-chatbot__canvas--Padding);
|
|
115
|
+
--pf-v6-c-drawer__head--PaddingBlockEnd: var(--pf-t--global--spacer--lg);
|
|
116
|
+
--pf-v6-c-drawer__head--PaddingInlineStart: var(--pf-chatbot__canvas--Padding);
|
|
117
|
+
--pf-v6-c-drawer__head--PaddingInlineEnd: var(--pf-chatbot__canvas--Padding);
|
|
118
|
+
|
|
119
|
+
.pf-v6-c-drawer__actions {
|
|
120
|
+
grid-column: 2;
|
|
121
|
+
justify-self: end;
|
|
122
|
+
align-self: start;
|
|
123
|
+
width: max-content;
|
|
124
|
+
--pf-v6-c-drawer__actions--MarginBlockStart: 0;
|
|
125
|
+
--pf-v6-c-drawer__actions--MarginInlineEnd: 0;
|
|
126
|
+
|
|
127
|
+
.pf-v6-c-button {
|
|
128
|
+
border-radius: var(--pf-t--global--border--radius--pill);
|
|
129
|
+
justify-content: center;
|
|
130
|
+
align-items: center;
|
|
131
|
+
|
|
132
|
+
&::before {
|
|
133
|
+
border-radius: inherit;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.pf-chatbot__canvas-editor {
|
|
141
|
+
display: flex;
|
|
142
|
+
flex: 1;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
min-height: 0;
|
|
145
|
+
height: 100%;
|
|
146
|
+
|
|
147
|
+
.pf-v6-c-code-editor {
|
|
148
|
+
--pf-v6-c-code-editor__main--BackgroundColor: var(--pf-chatbot__canvas--BackgroundColor);
|
|
149
|
+
}
|
|
150
|
+
}
|
package/src/Chatbot/Chatbot.scss
CHANGED
|
@@ -141,6 +141,19 @@
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
// ============================================================================
|
|
145
|
+
// Chatbot With Docked Nav
|
|
146
|
+
// ============================================================================
|
|
147
|
+
.pf-chatbot--docked-nav {
|
|
148
|
+
.pf-chatbot-container {
|
|
149
|
+
flex-direction: row;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.pf-chatbot__header-container {
|
|
153
|
+
display: none;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
144
157
|
.pf-chatbot-container--embedded {
|
|
145
158
|
min-height: 100%;
|
|
146
159
|
}
|
|
@@ -180,4 +193,3 @@
|
|
|
180
193
|
border: unset;
|
|
181
194
|
}
|
|
182
195
|
}
|
|
183
|
-
|
|
@@ -36,4 +36,15 @@ describe('Chatbot', () => {
|
|
|
36
36
|
);
|
|
37
37
|
expect(screen.getByTestId('chatbot')).toHaveClass('pf-m-compact');
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
it('should render docked navigation and apply the docked navigation class', () => {
|
|
41
|
+
render(
|
|
42
|
+
<Chatbot data-testid="chatbot" dockedNav={<nav aria-label="Chatbot navigation">Navigation</nav>}>
|
|
43
|
+
Chatbot Content
|
|
44
|
+
</Chatbot>
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(screen.getByTestId('chatbot')).toHaveClass('pf-chatbot--docked-nav');
|
|
48
|
+
expect(screen.getByRole('navigation', { name: 'Chatbot navigation' })).toBeInTheDocument();
|
|
49
|
+
});
|
|
39
50
|
});
|
package/src/Chatbot/Chatbot.tsx
CHANGED
|
@@ -20,6 +20,8 @@ export interface ChatbotProps {
|
|
|
20
20
|
ariaLabel?: string;
|
|
21
21
|
/** Density of information within the ChatBot */
|
|
22
22
|
isCompact?: boolean;
|
|
23
|
+
/** Optional navigation rail displayed before the chatbot content */
|
|
24
|
+
dockedNav?: React.ReactNode;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export enum ChatbotDisplayMode {
|
|
@@ -38,10 +40,11 @@ const ChatbotBase: FunctionComponent<ChatbotProps> = ({
|
|
|
38
40
|
innerRef,
|
|
39
41
|
ariaLabel,
|
|
40
42
|
isCompact,
|
|
43
|
+
dockedNav,
|
|
41
44
|
...props
|
|
42
45
|
}: ChatbotProps) => (
|
|
43
46
|
<div
|
|
44
|
-
className={`pf-chatbot pf-chatbot--${displayMode} ${!isVisible ? 'pf-chatbot--hidden' : 'pf-chatbot--visible'} ${isCompact ? 'pf-m-compact' : ''} ${className ?? ''}`}
|
|
47
|
+
className={`pf-chatbot pf-chatbot--${displayMode} ${dockedNav ? 'pf-chatbot--docked-nav' : ''} ${!isVisible ? 'pf-chatbot--hidden' : 'pf-chatbot--visible'} ${isCompact ? 'pf-m-compact' : ''} ${className ?? ''}`}
|
|
45
48
|
{...props}
|
|
46
49
|
>
|
|
47
50
|
{/* Ref is intended for use with skip to chatbot links, etc. */}
|
|
@@ -52,6 +55,7 @@ const ChatbotBase: FunctionComponent<ChatbotProps> = ({
|
|
|
52
55
|
tabIndex={-1}
|
|
53
56
|
ref={innerRef}
|
|
54
57
|
>
|
|
58
|
+
{dockedNav && <aside className="pf-chatbot__docked-nav">{dockedNav}</aside>}
|
|
55
59
|
{children}
|
|
56
60
|
</section>
|
|
57
61
|
) : undefined}
|