@patternfly/chatbot 6.9.0-prerelease.1 → 6.9.0-prerelease.11

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.
Files changed (67) hide show
  1. package/dist/cjs/Chatbot/Chatbot.d.ts +2 -0
  2. package/dist/cjs/Chatbot/Chatbot.js +2 -2
  3. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +1 -1
  4. package/dist/cjs/ChatbotFooter/ChatbotFootnote.js +1 -1
  5. package/dist/cjs/ChatbotModal/ChatbotModal.js +1 -1
  6. package/dist/cjs/ChatbotWelcomePrompt/ChatbotWelcomePrompt.js +1 -1
  7. package/dist/cjs/MessageBar/MessageBar.d.ts +1 -1
  8. package/dist/cjs/MessageBar/MessageBar.js +4 -3
  9. package/dist/cjs/MessageBar/SendButton.js +2 -2
  10. package/dist/cjs/SourcesCard/SourcesCard.d.ts +2 -0
  11. package/dist/cjs/SourcesCardBase/SourcesCardBase.d.ts +2 -0
  12. package/dist/cjs/SourcesCardBase/SourcesCardBase.js +2 -2
  13. package/dist/css/main.css +201 -66
  14. package/dist/css/main.css.map +1 -1
  15. package/dist/esm/Chatbot/Chatbot.d.ts +2 -0
  16. package/dist/esm/Chatbot/Chatbot.js +3 -3
  17. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +1 -1
  18. package/dist/esm/ChatbotFooter/ChatbotFootnote.js +1 -1
  19. package/dist/esm/ChatbotModal/ChatbotModal.js +1 -1
  20. package/dist/esm/ChatbotWelcomePrompt/ChatbotWelcomePrompt.js +1 -1
  21. package/dist/esm/MessageBar/MessageBar.d.ts +1 -1
  22. package/dist/esm/MessageBar/MessageBar.js +4 -3
  23. package/dist/esm/MessageBar/SendButton.js +2 -2
  24. package/dist/esm/SourcesCard/SourcesCard.d.ts +2 -0
  25. package/dist/esm/SourcesCardBase/SourcesCardBase.d.ts +2 -0
  26. package/dist/esm/SourcesCardBase/SourcesCardBase.js +2 -2
  27. package/package.json +9 -9
  28. package/patternfly-docs/content/extensions/chatbot/chatbot.md +1 -0
  29. package/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +1 -1
  30. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +31 -0
  31. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +2 -0
  32. package/patternfly-docs/content/extensions/chatbot/examples/demos/Canvas.md +132 -0
  33. package/patternfly-docs/content/extensions/chatbot/examples/demos/Canvas.tsx +615 -0
  34. package/patternfly-docs/content/extensions/chatbot/examples/demos/CanvasWithDockedNav.tsx +697 -0
  35. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +39 -5
  36. package/patternfly-docs/content/extensions/chatbot/examples/demos/FullscreenDockedNav.tsx +385 -0
  37. package/patternfly-docs/patternfly-docs.config.js +1 -1
  38. package/src/Canvas/Canvas.scss +150 -0
  39. package/src/Chatbot/Chatbot.scss +13 -1
  40. package/src/Chatbot/Chatbot.test.tsx +11 -0
  41. package/src/Chatbot/Chatbot.tsx +5 -1
  42. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +91 -77
  43. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +32 -13
  44. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +1 -0
  45. package/src/ChatbotFooter/ChatbotFooternote.test.tsx +50 -0
  46. package/src/ChatbotFooter/ChatbotFootnote.tsx +1 -1
  47. package/src/ChatbotModal/ChatbotModal.scss +2 -7
  48. package/src/ChatbotModal/ChatbotModal.tsx +1 -0
  49. package/src/ChatbotToggle/ChatbotToggle.scss +3 -2
  50. package/src/ChatbotToggle/ChatbotToggle.test.tsx +4 -0
  51. package/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.scss +9 -0
  52. package/src/ChatbotWelcomePrompt/ChatbotWelcomePrompt.tsx +4 -2
  53. package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +2 -6
  54. package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +2 -6
  55. package/src/Message/Message.scss +9 -0
  56. package/src/MessageBar/AttachButton.scss +4 -21
  57. package/src/MessageBar/MessageBar.scss +2 -2
  58. package/src/MessageBar/MessageBar.test.tsx +20 -0
  59. package/src/MessageBar/MessageBar.tsx +7 -3
  60. package/src/MessageBar/SendButton.scss +1 -32
  61. package/src/MessageBar/SendButton.tsx +2 -2
  62. package/src/SourcesCard/SourcesCard.scss +6 -0
  63. package/src/SourcesCard/SourcesCard.test.tsx +16 -0
  64. package/src/SourcesCard/SourcesCard.tsx +2 -0
  65. package/src/SourcesCardBase/SourcesCardBase.test.tsx +62 -0
  66. package/src/SourcesCardBase/SourcesCardBase.tsx +8 -0
  67. 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 { BarsIcon } from '@patternfly/react-icons/dist/esm/icons/bars-icon';
53
- import { RhUiBuildFillIcon, RhUiCopyFillIcon } from '@patternfly/react-icons';
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
+ };
@@ -1,6 +1,6 @@
1
1
  // This module is shared between NodeJS and babelled ES5
2
2
  module.exports = {
3
- sideNavItems: [{ section: 'extensions' }],
3
+ sideNavItems: [{ section: 'extensions' }, { section: 'patterns' }],
4
4
  topNavItems: [],
5
5
  hasThemeSwitcher: true,
6
6
  hasRTLSwitcher: true,
@@ -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
+ }
@@ -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
  });
@@ -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}