@patternfly/chatbot 6.3.0-prerelease.2 → 6.3.0-prerelease.4

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 (91) hide show
  1. package/dist/cjs/AttachmentEdit/AttachmentEdit.d.ts +2 -0
  2. package/dist/cjs/AttachmentEdit/AttachmentEdit.js +2 -2
  3. package/dist/cjs/Chatbot/Chatbot.d.ts +2 -0
  4. package/dist/cjs/Chatbot/Chatbot.js +2 -2
  5. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +2 -0
  6. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +2 -2
  7. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +4 -0
  8. package/dist/cjs/ChatbotHeader/ChatbotHeader.js +1 -1
  9. package/dist/cjs/ChatbotHeader/ChatbotHeaderCloseButton.d.ts +4 -2
  10. package/dist/cjs/ChatbotHeader/ChatbotHeaderCloseButton.js +20 -6
  11. package/dist/cjs/ChatbotHeader/ChatbotHeaderCloseButton.test.d.ts +1 -1
  12. package/dist/cjs/ChatbotHeader/ChatbotHeaderCloseButton.test.js +5 -0
  13. package/dist/cjs/ChatbotHeader/ChatbotHeaderMain.js +1 -1
  14. package/dist/cjs/ChatbotModal/ChatbotModal.d.ts +3 -0
  15. package/dist/cjs/ChatbotModal/ChatbotModal.js +2 -2
  16. package/dist/cjs/ChatbotModal/ChatbotModal.test.d.ts +1 -0
  17. package/dist/cjs/ChatbotModal/ChatbotModal.test.js +28 -0
  18. package/dist/cjs/CodeModal/CodeModal.d.ts +2 -0
  19. package/dist/cjs/CodeModal/CodeModal.js +3 -3
  20. package/dist/cjs/CodeModal/CodeModal.test.d.ts +1 -0
  21. package/dist/cjs/CodeModal/CodeModal.test.js +15 -0
  22. package/dist/cjs/FileDetails/FileDetails.js +1 -1
  23. package/dist/cjs/PreviewAttachment/PreviewAttachment.d.ts +2 -0
  24. package/dist/cjs/PreviewAttachment/PreviewAttachment.js +2 -2
  25. package/dist/cjs/Settings/SettingsForm.d.ts +2 -0
  26. package/dist/cjs/Settings/SettingsForm.js +2 -2
  27. package/dist/cjs/Settings/SettingsForm.test.d.ts +1 -1
  28. package/dist/cjs/Settings/SettingsForm.test.js +12 -0
  29. package/dist/css/main.css +54 -9
  30. package/dist/css/main.css.map +1 -1
  31. package/dist/esm/AttachmentEdit/AttachmentEdit.d.ts +2 -0
  32. package/dist/esm/AttachmentEdit/AttachmentEdit.js +2 -2
  33. package/dist/esm/Chatbot/Chatbot.d.ts +2 -0
  34. package/dist/esm/Chatbot/Chatbot.js +2 -2
  35. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.d.ts +2 -0
  36. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +2 -2
  37. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.js +4 -0
  38. package/dist/esm/ChatbotHeader/ChatbotHeader.js +1 -1
  39. package/dist/esm/ChatbotHeader/ChatbotHeaderCloseButton.d.ts +4 -2
  40. package/dist/esm/ChatbotHeader/ChatbotHeaderCloseButton.js +20 -6
  41. package/dist/esm/ChatbotHeader/ChatbotHeaderCloseButton.test.d.ts +1 -1
  42. package/dist/esm/ChatbotHeader/ChatbotHeaderCloseButton.test.js +5 -0
  43. package/dist/esm/ChatbotHeader/ChatbotHeaderMain.js +1 -1
  44. package/dist/esm/ChatbotModal/ChatbotModal.d.ts +3 -0
  45. package/dist/esm/ChatbotModal/ChatbotModal.js +2 -2
  46. package/dist/esm/ChatbotModal/ChatbotModal.test.d.ts +1 -0
  47. package/dist/esm/ChatbotModal/ChatbotModal.test.js +23 -0
  48. package/dist/esm/CodeModal/CodeModal.d.ts +2 -0
  49. package/dist/esm/CodeModal/CodeModal.js +3 -3
  50. package/dist/esm/CodeModal/CodeModal.test.d.ts +1 -0
  51. package/dist/esm/CodeModal/CodeModal.test.js +10 -0
  52. package/dist/esm/FileDetails/FileDetails.js +1 -1
  53. package/dist/esm/PreviewAttachment/PreviewAttachment.d.ts +2 -0
  54. package/dist/esm/PreviewAttachment/PreviewAttachment.js +2 -2
  55. package/dist/esm/Settings/SettingsForm.d.ts +2 -0
  56. package/dist/esm/Settings/SettingsForm.js +2 -2
  57. package/dist/esm/Settings/SettingsForm.test.d.ts +1 -1
  58. package/dist/esm/Settings/SettingsForm.test.js +12 -0
  59. package/dist/tsconfig.tsbuildinfo +1 -1
  60. package/package.json +1 -1
  61. package/patternfly-docs/content/extensions/chatbot/examples/Messages/AttachmentEdit.tsx +10 -1
  62. package/patternfly-docs/content/extensions/chatbot/examples/Messages/PreviewAttachment.tsx +10 -1
  63. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +9 -0
  64. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithActions.tsx +9 -0
  65. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSelection.tsx +11 -2
  66. package/patternfly-docs/content/extensions/chatbot/examples/UI/CompactSettings.tsx +289 -0
  67. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +7 -0
  68. package/src/AttachmentEdit/AttachmentEdit.tsx +5 -1
  69. package/src/Chatbot/Chatbot.scss +7 -0
  70. package/src/Chatbot/Chatbot.tsx +4 -1
  71. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.scss +28 -2
  72. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.test.tsx +18 -0
  73. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +4 -1
  74. package/src/ChatbotHeader/ChatbotHeader.scss +8 -0
  75. package/src/ChatbotHeader/ChatbotHeader.tsx +1 -1
  76. package/src/ChatbotHeader/ChatbotHeaderCloseButton.test.tsx +6 -0
  77. package/src/ChatbotHeader/ChatbotHeaderCloseButton.tsx +12 -6
  78. package/src/ChatbotHeader/ChatbotHeaderMain.tsx +3 -1
  79. package/src/ChatbotModal/ChatbotModal.scss +15 -4
  80. package/src/ChatbotModal/ChatbotModal.test.tsx +59 -0
  81. package/src/ChatbotModal/ChatbotModal.tsx +5 -1
  82. package/src/CodeModal/CodeModal.scss +11 -3
  83. package/src/CodeModal/CodeModal.test.tsx +24 -0
  84. package/src/CodeModal/CodeModal.tsx +6 -2
  85. package/src/FileDetails/FileDetails.tsx +1 -1
  86. package/src/FileDetails/__snapshots__/FileDetails.test.tsx.snap +1 -1
  87. package/src/FileDetailsLabel/__snapshots__/FileDetailsLabel.test.tsx.snap +1 -1
  88. package/src/PreviewAttachment/PreviewAttachment.tsx +5 -1
  89. package/src/Settings/Settings.scss +11 -0
  90. package/src/Settings/SettingsForm.test.tsx +17 -0
  91. package/src/Settings/SettingsForm.tsx +12 -2
@@ -0,0 +1,289 @@
1
+ import React from 'react';
2
+
3
+ import SettingsForm from '@patternfly/chatbot/dist/dynamic/Settings';
4
+ import {
5
+ Button,
6
+ Divider,
7
+ Dropdown,
8
+ DropdownGroup,
9
+ DropdownItem,
10
+ DropdownList,
11
+ MenuToggle,
12
+ MenuToggleElement,
13
+ Switch,
14
+ Title
15
+ } from '@patternfly/react-core';
16
+ import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
17
+ import ChatbotHeader, {
18
+ ChatbotHeaderActions,
19
+ ChatbotHeaderCloseButton,
20
+ ChatbotHeaderMain,
21
+ ChatbotHeaderOptionsDropdown,
22
+ ChatbotHeaderTitle
23
+ } from '@patternfly/chatbot/dist/dynamic/ChatbotHeader';
24
+ import { CogIcon, ExpandIcon, OpenDrawerRightIcon, OutlinedWindowRestoreIcon } from '@patternfly/react-icons';
25
+
26
+ export const CompactSettingsDemo: React.FunctionComponent = () => {
27
+ const [isChecked, setIsChecked] = React.useState<boolean>(true);
28
+ const [isThemeOpen, setIsThemeOpen] = React.useState(false);
29
+ const [isLanguageOpen, setIsLanguageOpen] = React.useState(false);
30
+ const [isVoiceOpen, setIsVoiceOpen] = React.useState(false);
31
+ const [displayMode, setDisplayMode] = React.useState(ChatbotDisplayMode.default);
32
+ const [areSettingsOpen, setAreSettingsOpen] = React.useState(true);
33
+ const chatbotVisible = true;
34
+
35
+ const onFocus = (id: string) => {
36
+ const element = document.getElementById(id);
37
+ (element as HTMLElement).focus();
38
+ };
39
+
40
+ const onThemeToggleClick = () => {
41
+ setIsThemeOpen(!isThemeOpen);
42
+ };
43
+
44
+ const onThemeSelect = (
45
+ _event: React.MouseEvent<Element, MouseEvent> | undefined,
46
+ value: string | number | undefined
47
+ ) => {
48
+ // eslint-disable-next-line no-console
49
+ console.log('selected', value);
50
+ onFocus('theme');
51
+ setIsThemeOpen(false);
52
+ };
53
+
54
+ const onLanguageToggleClick = () => {
55
+ setIsLanguageOpen(!isLanguageOpen);
56
+ };
57
+
58
+ const onLanguageSelect = (
59
+ _event: React.MouseEvent<Element, MouseEvent> | undefined,
60
+ value: string | number | undefined
61
+ ) => {
62
+ // eslint-disable-next-line no-console
63
+ console.log('selected', value);
64
+ onFocus('language');
65
+ setIsLanguageOpen(false);
66
+ };
67
+
68
+ const onVoiceToggleClick = () => {
69
+ onFocus('voice');
70
+ setIsVoiceOpen(!isVoiceOpen);
71
+ };
72
+
73
+ const onVoiceSelect = (
74
+ _event: React.MouseEvent<Element, MouseEvent> | undefined,
75
+ value: string | number | undefined
76
+ ) => {
77
+ // eslint-disable-next-line no-console
78
+ console.log('selected', value);
79
+ setIsVoiceOpen(false);
80
+ };
81
+
82
+ const handleChange = (_event: React.FormEvent<HTMLInputElement>, checked: boolean) => {
83
+ setIsChecked(checked);
84
+ };
85
+
86
+ const themeDropdown = (
87
+ <Dropdown
88
+ isOpen={isThemeOpen}
89
+ onSelect={onThemeSelect}
90
+ onOpenChange={(isOpen: boolean) => setIsThemeOpen(isOpen)}
91
+ shouldFocusToggleOnSelect
92
+ shouldFocusFirstItemOnOpen
93
+ shouldPreventScrollOnItemFocus
94
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
95
+ // We want to add the id property here as well so the label is coupled
96
+ // with t he button on screen readers.
97
+ <MenuToggle size="sm" id="theme" ref={toggleRef} onClick={onThemeToggleClick} isExpanded={isThemeOpen}>
98
+ System
99
+ </MenuToggle>
100
+ )}
101
+ ouiaId="ThemeDropdown"
102
+ >
103
+ <DropdownList>
104
+ <DropdownItem value="System" key="system">
105
+ System
106
+ </DropdownItem>
107
+ </DropdownList>
108
+ </Dropdown>
109
+ );
110
+
111
+ const languageDropdown = (
112
+ <Dropdown
113
+ isOpen={isLanguageOpen}
114
+ onSelect={onLanguageSelect}
115
+ onOpenChange={(isOpen: boolean) => setIsLanguageOpen(isOpen)}
116
+ shouldFocusToggleOnSelect
117
+ shouldFocusFirstItemOnOpen
118
+ shouldPreventScrollOnItemFocus
119
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
120
+ // We want to add the id property here as well so the label is coupled
121
+ // with the button on screen readers.
122
+ <MenuToggle size="sm" id="language" ref={toggleRef} onClick={onLanguageToggleClick} isExpanded={isLanguageOpen}>
123
+ Auto-detect
124
+ </MenuToggle>
125
+ )}
126
+ ouiaId="LanguageDropdown"
127
+ >
128
+ <DropdownList>
129
+ <DropdownItem value="Auto-detect" key="auto-detect">
130
+ Auto-detect
131
+ </DropdownItem>
132
+ </DropdownList>
133
+ </Dropdown>
134
+ );
135
+ const voiceDropdown = (
136
+ <Dropdown
137
+ isOpen={isVoiceOpen}
138
+ onSelect={onVoiceSelect}
139
+ onOpenChange={(isOpen: boolean) => setIsVoiceOpen(isOpen)}
140
+ shouldFocusToggleOnSelect
141
+ shouldFocusFirstItemOnOpen
142
+ shouldPreventScrollOnItemFocus
143
+ toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
144
+ // We want to add the id property here as well so the label is coupled
145
+ // with the button on screen readers.
146
+ <MenuToggle size="sm" id="voice" ref={toggleRef} onClick={onVoiceToggleClick} isExpanded={isVoiceOpen}>
147
+ Bot
148
+ </MenuToggle>
149
+ )}
150
+ ouiaId="VoiceDropdown"
151
+ >
152
+ <DropdownList>
153
+ <DropdownItem value="Bot" key="bot">
154
+ Bot
155
+ </DropdownItem>
156
+ </DropdownList>
157
+ </Dropdown>
158
+ );
159
+ const children = [
160
+ { id: 'theme', label: 'Theme', field: themeDropdown },
161
+ { id: 'language', label: 'Language', field: languageDropdown },
162
+ { id: 'voice', label: 'Voice', field: voiceDropdown },
163
+ {
164
+ id: 'analytics',
165
+ label: 'Share analytics',
166
+ field: (
167
+ <Switch
168
+ // We want to add the id property here as well so the label is coupled
169
+ // with the button on screen readers.
170
+ id="analytics"
171
+ aria-label="Togglable option for whether to share analytics"
172
+ isChecked={isChecked}
173
+ onChange={handleChange}
174
+ />
175
+ )
176
+ },
177
+ {
178
+ id: 'archived-chat',
179
+ label: 'Archive chat',
180
+ field: (
181
+ // We want to add the id property here as well so the label is coupled
182
+ // with the button on screen readers.
183
+ <Button size="sm" id="archived-chat" variant="secondary">
184
+ Manage
185
+ </Button>
186
+ )
187
+ },
188
+ {
189
+ id: 'archive-all',
190
+ label: 'Archive all chat',
191
+ field: (
192
+ // We want to add the id property here as well so the label is coupled
193
+ // with the button on screen readers.
194
+ <Button size="sm" id="archive-all" variant="secondary">
195
+ Archive all
196
+ </Button>
197
+ )
198
+ },
199
+ {
200
+ id: 'delete-all',
201
+ label: 'Delete all chats',
202
+ field: (
203
+ // We want to add the id property here as well so the label is coupled
204
+ // with the button on screen readers.
205
+ <Button size="sm" id="delete-all" variant="danger">
206
+ Delete all
207
+ </Button>
208
+ )
209
+ }
210
+ ];
211
+
212
+ const onSelectDropdownItem = (
213
+ _event: React.MouseEvent<Element, MouseEvent> | undefined,
214
+ value: string | number | undefined
215
+ ) => {
216
+ if (value === 'Settings') {
217
+ setAreSettingsOpen(true);
218
+ } else {
219
+ setDisplayMode(value as ChatbotDisplayMode);
220
+ }
221
+ };
222
+
223
+ const regularChatbot = (
224
+ <ChatbotHeader>
225
+ <ChatbotHeaderActions>
226
+ <ChatbotHeaderOptionsDropdown isCompact onSelect={onSelectDropdownItem}>
227
+ <DropdownGroup label="Display mode">
228
+ <DropdownList>
229
+ <DropdownItem
230
+ value={ChatbotDisplayMode.default}
231
+ key="switchDisplayOverlay"
232
+ icon={<OutlinedWindowRestoreIcon aria-hidden />}
233
+ isSelected={displayMode === ChatbotDisplayMode.default}
234
+ >
235
+ <span>Overlay</span>
236
+ </DropdownItem>
237
+ <DropdownItem
238
+ value={ChatbotDisplayMode.docked}
239
+ key="switchDisplayDock"
240
+ icon={<OpenDrawerRightIcon aria-hidden />}
241
+ isSelected={displayMode === ChatbotDisplayMode.docked}
242
+ >
243
+ <span>Dock to window</span>
244
+ </DropdownItem>
245
+ <DropdownItem
246
+ value={ChatbotDisplayMode.fullscreen}
247
+ key="switchDisplayFullscreen"
248
+ icon={<ExpandIcon aria-hidden />}
249
+ isSelected={displayMode === ChatbotDisplayMode.fullscreen}
250
+ >
251
+ <span>Fullscreen</span>
252
+ </DropdownItem>
253
+ </DropdownList>
254
+ </DropdownGroup>
255
+ <Divider />
256
+ <DropdownList>
257
+ <DropdownItem value="Settings" key="switchSettings" icon={<CogIcon aria-hidden />}>
258
+ <span>Settings</span>
259
+ </DropdownItem>
260
+ </DropdownList>
261
+ </ChatbotHeaderOptionsDropdown>
262
+ </ChatbotHeaderActions>
263
+ </ChatbotHeader>
264
+ );
265
+
266
+ return (
267
+ <>
268
+ <Chatbot isCompact isVisible={chatbotVisible} displayMode={displayMode}>
269
+ {areSettingsOpen ? (
270
+ <>
271
+ <ChatbotHeader className="pf-m-compact">
272
+ <ChatbotHeaderMain>
273
+ <ChatbotHeaderTitle>
274
+ <Title headingLevel="h1" size="2xl">
275
+ Settings
276
+ </Title>
277
+ </ChatbotHeaderTitle>
278
+ </ChatbotHeaderMain>
279
+ <ChatbotHeaderCloseButton isCompact onClick={() => setAreSettingsOpen(false)} />
280
+ </ChatbotHeader>
281
+ <SettingsForm isCompact fields={children} />
282
+ </>
283
+ ) : (
284
+ <>{regularChatbot}</>
285
+ )}
286
+ </Chatbot>
287
+ </>
288
+ );
289
+ };
@@ -406,6 +406,13 @@ In this demo, you can toggle the settings page by clicking the "Settings" button
406
406
 
407
407
  ```
408
408
 
409
+ ### Compact settings
410
+
411
+ To make the settings menu compact, with less spacing between the menu contents, pass `isCompact` to the `<SettingsForm>`.
412
+ ```js file="./CompactSettings.tsx" isFullscreen
413
+
414
+ ```
415
+
409
416
  ## Modals
410
417
 
411
418
  ### Modal
@@ -22,6 +22,8 @@ export interface AttachmentEditProps {
22
22
  title?: string;
23
23
  /** Display mode for the Chatbot parent; this influences the styles applied */
24
24
  displayMode?: ChatbotDisplayMode;
25
+ /** Sets modal to compact styling. */
26
+ isCompact?: boolean;
25
27
  }
26
28
 
27
29
  export const AttachmentEdit: React.FunctionComponent<AttachmentEditProps> = ({
@@ -32,7 +34,8 @@ export const AttachmentEdit: React.FunctionComponent<AttachmentEditProps> = ({
32
34
  onCancel,
33
35
  onSave,
34
36
  title = 'Edit attachment',
35
- displayMode = ChatbotDisplayMode.default
37
+ displayMode = ChatbotDisplayMode.default,
38
+ isCompact
36
39
  }: AttachmentEditProps) => {
37
40
  const handleSave = (_event: React.MouseEvent | MouseEvent | KeyboardEvent, code) => {
38
41
  handleModalToggle(_event);
@@ -56,6 +59,7 @@ export const AttachmentEdit: React.FunctionComponent<AttachmentEditProps> = ({
56
59
  secondaryActionBtn="Cancel"
57
60
  title={title}
58
61
  displayMode={displayMode}
62
+ isCompact={isCompact}
59
63
  />
60
64
  );
61
65
  };
@@ -125,3 +125,10 @@
125
125
  border-radius: var(--pf-t--global--border--radius--sharp);
126
126
  }
127
127
  }
128
+
129
+ // ============================================================================
130
+ // Information density styles
131
+ // ============================================================================
132
+ .pf-chatbot.pf-m-compact {
133
+ font-size: var(--pf-t--global--font--size--sm);
134
+ }
@@ -17,6 +17,8 @@ export interface ChatbotProps {
17
17
  innerRef?: React.Ref<HTMLDivElement>;
18
18
  /** Custom aria label applied to focusable container */
19
19
  ariaLabel?: string;
20
+ /** Density of information within the ChatBot */
21
+ isCompact?: boolean;
20
22
  }
21
23
 
22
24
  export enum ChatbotDisplayMode {
@@ -34,6 +36,7 @@ const ChatbotBase: React.FunctionComponent<ChatbotProps> = ({
34
36
  className,
35
37
  innerRef,
36
38
  ariaLabel,
39
+ isCompact,
37
40
  ...props
38
41
  }: ChatbotProps) => {
39
42
  // Configure animations
@@ -44,7 +47,7 @@ const ChatbotBase: React.FunctionComponent<ChatbotProps> = ({
44
47
 
45
48
  return (
46
49
  <motion.div
47
- className={`pf-chatbot pf-chatbot--${displayMode} ${!isVisible ? 'pf-chatbot--hidden' : ''} ${className ?? ''}`}
50
+ className={`pf-chatbot pf-chatbot--${displayMode} ${!isVisible ? 'pf-chatbot--hidden' : ''} ${isCompact ? 'pf-m-compact' : ''} ${className ?? ''}`}
48
51
  variants={motionChatbot}
49
52
  initial="hidden"
50
53
  animate={isVisible ? 'visible' : 'hidden'}
@@ -9,8 +9,8 @@
9
9
  // Drawer input
10
10
  // ----------------------------------------------------------------------------
11
11
  .pf-chatbot__input {
12
- padding-inline-start: var(--pf-t--global--spacer--xl);
13
- padding-inline-end: var(--pf-t--global--spacer--xl);
12
+ padding-inline-start: var(--pf-t--global--spacer--lg);
13
+ padding-inline-end: var(--pf-t--global--spacer--lg);
14
14
  }
15
15
 
16
16
  // Drawer menu
@@ -205,3 +205,29 @@
205
205
  gap: var(--pf-t--global--spacer--sm);
206
206
  align-self: stretch;
207
207
  }
208
+
209
+ .pf-chatbot__history.pf-m-compact {
210
+ .pf-v6-c-drawer__main {
211
+ .pf-v6-c-drawer__panel {
212
+ --pf-v6-c-drawer__panel--PaddingBlockStart: var(--pf-t--global--spacer--sm);
213
+ --pf-v6-c-drawer__panel--PaddingBlockEnd: var(--pf-t--global--spacer--sm);
214
+ --pf-v6-c-drawer__head--PaddingInlineStart: var(--pf-t--global--spacer--md);
215
+ --pf-v6-c-drawer__head--PaddingInlineEnd: var(--pf-t--global--spacer--md);
216
+ --pf-v6-c-drawer__panel--RowGap: var(--pf-t--global--spacer--md);
217
+ }
218
+
219
+ .pf-v6-c-drawer__body {
220
+ --pf-v6-c-drawer__panel__body--PaddingInlineStart: var(--pf-t--global--spacer--md);
221
+ --pf-v6-c-drawer__panel__body--PaddingInlineEnd: var(--pf-t--global--spacer--md);
222
+ }
223
+ }
224
+
225
+ .pf-chatbot__menu-item {
226
+ font-size: var(--pf-t--global--font--size--body--md);
227
+ }
228
+
229
+ .pf-v6-c-drawer__head {
230
+ --pf-v6-c-drawer__head--PaddingInlineStart: var(--pf-t--global--spacer--lg);
231
+ --pf-v6-c-drawer__head--PaddingInlineEnd: var(--pf-t--global--spacer--lg);
232
+ }
233
+ }
@@ -415,4 +415,22 @@ describe('ChatbotConversationHistoryNav', () => {
415
415
  })
416
416
  ).toBeTruthy();
417
417
  });
418
+
419
+ it('should handle isCompact', () => {
420
+ render(
421
+ <ChatbotConversationHistoryNav
422
+ onDrawerToggle={onDrawerToggle}
423
+ isDrawerOpen={true}
424
+ displayMode={ChatbotDisplayMode.fullscreen}
425
+ setIsDrawerOpen={jest.fn()}
426
+ reverseButtonOrder={false}
427
+ handleTextInputChange={jest.fn()}
428
+ conversations={initialConversations}
429
+ noResultsState={NO_RESULTS}
430
+ isCompact
431
+ data-testid="drawer"
432
+ />
433
+ );
434
+ expect(screen.getByTestId('drawer')).toHaveClass('pf-m-compact');
435
+ });
418
436
  });
@@ -116,6 +116,8 @@ export interface ChatbotConversationHistoryNavProps extends DrawerProps {
116
116
  emptyState?: HistoryEmptyStateProps;
117
117
  /** Content to show in no results state. No results state will appear once content is passed in. */
118
118
  noResultsState?: HistoryEmptyStateProps;
119
+ /** Sets drawer to compact styling. */
120
+ isCompact?: boolean;
119
121
  }
120
122
 
121
123
  export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConversationHistoryNavProps> = ({
@@ -147,6 +149,7 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
147
149
  errorState,
148
150
  emptyState,
149
151
  noResultsState,
152
+ isCompact,
150
153
  ...props
151
154
  }: ChatbotConversationHistoryNavProps) => {
152
155
  const drawerRef = React.useRef<HTMLDivElement>(null);
@@ -287,7 +290,7 @@ export const ChatbotConversationHistoryNav: React.FunctionComponent<ChatbotConve
287
290
 
288
291
  return (
289
292
  <Drawer
290
- className="pf-chatbot__history"
293
+ className={`pf-chatbot__history ${isCompact ? 'pf-m-compact' : ''}`}
291
294
  isExpanded={isDrawerOpen}
292
295
  onExpand={onExpand}
293
296
  position="start"
@@ -137,3 +137,11 @@
137
137
  :where(.pf-v6-theme-dark) .show-dark .pf-m-picture {
138
138
  display: inline-flex;
139
139
  }
140
+
141
+ // ============================================================================
142
+ // Information density styles
143
+ // ============================================================================
144
+ .pf-chatbot__button--toggle-menu.pf-m-compact {
145
+ width: 2rem;
146
+ height: 2rem;
147
+ }
@@ -14,7 +14,7 @@ export const ChatbotHeader: React.FunctionComponent<ChatbotHeaderProps> = ({
14
14
  children
15
15
  }: ChatbotHeaderProps) => (
16
16
  <div className="pf-chatbot__header-container">
17
- <div className={`pf-chatbot__header ${className ?? ''}`}>{children}</div>
17
+ <div className={`pf-chatbot__header${className ? ` ${className}` : ''}`}>{children}</div>
18
18
  <Divider className="pf-chatbot__header__divider" />
19
19
  </div>
20
20
  );
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { fireEvent, render, screen } from '@testing-library/react';
3
3
  import { ChatbotHeaderCloseButton } from './ChatbotHeaderCloseButton';
4
+ import '@testing-library/jest-dom';
4
5
 
5
6
  describe('ChatbotHeaderCloseButton', () => {
6
7
  it('should render ChatbotHeaderCloseButton', () => {
@@ -17,4 +18,9 @@ describe('ChatbotHeaderCloseButton', () => {
17
18
  fireEvent.click(screen.getByRole('button', { name: 'Close' }));
18
19
  expect(onClick).toHaveBeenCalled();
19
20
  });
21
+
22
+ it('should render button with isCompact', () => {
23
+ render(<ChatbotHeaderCloseButton data-testid="close-button" onClick={jest.fn()} isCompact />);
24
+ expect(screen.getByTestId('close-button')).toHaveClass('pf-m-compact');
25
+ });
20
26
  });
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
2
 
3
- import { Button, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
3
+ import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
4
4
  import { CloseIcon } from '@patternfly/react-icons';
5
5
 
6
- export interface ChatbotHeaderCloseButtonProps {
6
+ export interface ChatbotHeaderCloseButtonProps extends ButtonProps {
7
7
  /** Callback function for when button is clicked */
8
8
  onClick: () => void;
9
9
  /** Custom classname for the header component */
@@ -16,6 +16,8 @@ export interface ChatbotHeaderCloseButtonProps {
16
16
  innerRef?: React.Ref<HTMLButtonElement>;
17
17
  /** Content used in tooltip */
18
18
  tooltipContent?: string;
19
+ /** Sets button to compact styling. */
20
+ isCompact?: boolean;
19
21
  }
20
22
 
21
23
  const ChatbotHeaderCloseButtonBase: React.FunctionComponent<ChatbotHeaderCloseButtonProps> = ({
@@ -24,9 +26,11 @@ const ChatbotHeaderCloseButtonBase: React.FunctionComponent<ChatbotHeaderCloseBu
24
26
  tooltipProps,
25
27
  menuAriaLabel = 'Close',
26
28
  innerRef,
27
- tooltipContent = 'Close'
29
+ tooltipContent = 'Close',
30
+ isCompact,
31
+ ...props
28
32
  }: ChatbotHeaderCloseButtonProps) => (
29
- <div className={`pf-chatbot__menu ${className}`}>
33
+ <div className={`pf-chatbot__menu${className ? ` ${className}` : ''}`}>
30
34
  <Tooltip
31
35
  content={tooltipContent}
32
36
  position="bottom"
@@ -35,16 +39,18 @@ const ChatbotHeaderCloseButtonBase: React.FunctionComponent<ChatbotHeaderCloseBu
35
39
  {...tooltipProps}
36
40
  >
37
41
  <Button
38
- className="pf-chatbot__button--toggle-menu"
42
+ className={`pf-chatbot__button--toggle-menu ${isCompact ? 'pf-m-compact' : ''}`}
39
43
  variant="plain"
40
44
  onClick={onClick}
41
45
  aria-label={menuAriaLabel}
42
46
  ref={innerRef}
43
47
  icon={
44
- <Icon size="xl" isInline>
48
+ <Icon size={isCompact ? 'lg' : 'xl'} isInline>
45
49
  <CloseIcon />
46
50
  </Icon>
47
51
  }
52
+ size={isCompact ? 'sm' : undefined}
53
+ {...props}
48
54
  />
49
55
  </Tooltip>
50
56
  </div>
@@ -10,6 +10,8 @@ export interface ChatbotHeaderMainProps {
10
10
  export const ChatbotHeaderMain: React.FunctionComponent<ChatbotHeaderMainProps> = ({
11
11
  className,
12
12
  children
13
- }: ChatbotHeaderMainProps) => <div className={`pf-chatbot__header-main ${className}`}>{children}</div>;
13
+ }: ChatbotHeaderMainProps) => (
14
+ <div className={`pf-chatbot__header-main${className ? ` ${className}` : ''}`}>{children}</div>
15
+ );
14
16
 
15
17
  export default ChatbotHeaderMain;
@@ -14,10 +14,7 @@
14
14
  .pf-v6-c-modal-box__title {
15
15
  --pf-v6-c-modal-box__title--FontSize: var(--pf-t--global--font--size--heading--h3);
16
16
  }
17
- .pf-v6-c-button.pf-m-primary.pf-m-block,
18
- .pf-v6-c-button.pf-m-link.pf-m-block {
19
- --pf-v6-c-button--FontWeight: var(--pf-t--global--font--weight--body--bold);
20
- }
17
+
21
18
  .pf-v6-c-modal-box__footer {
22
19
  padding-block-start: var(--pf-t--global--spacer--xl);
23
20
  padding-block-end: var(--pf-t--global--spacer--xl);
@@ -91,3 +88,17 @@
91
88
  .pf-v6-c-backdrop.pf-chatbot__backdrop {
92
89
  position: absolute;
93
90
  }
91
+
92
+ // ============================================================================
93
+ // Compact
94
+ // ============================================================================
95
+ .pf-chatbot__chatbot-modal.pf-m-compact {
96
+ .pf-v6-c-modal-box__header {
97
+ padding-block-end: 0;
98
+ }
99
+
100
+ .pf-v6-c-modal-box__footer {
101
+ padding-block-start: var(--pf-t--global--spacer--lg);
102
+ padding-block-end: var(--pf-t--global--spacer--lg);
103
+ }
104
+ }
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import ChatbotModal from './ChatbotModal';
4
+ import '@testing-library/jest-dom';
5
+ import { ModalBody, ModalHeader } from '@patternfly/react-core';
6
+
7
+ describe('ChatbotModal', () => {
8
+ it('should render compact modal', () => {
9
+ render(
10
+ <ChatbotModal data-testid="modal" isCompact isOpen>
11
+ <ModalHeader
12
+ title="Modal with description"
13
+ labelId="modal-with-description-title"
14
+ description="A description is used when you want to provide more info about the modal than the title is able to describe. The content in the description is static and will not scroll with the rest of the modal body."
15
+ />
16
+ <ModalBody tabIndex={0} id="modal-box-body-with-description">
17
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
18
+ magna aliqua. Quis eleifend quam adipiscing vitae proin sagittis nisl rhoncus. Semper auctor neque vitae
19
+ tempus. Diam donec adipiscing tristique risus. Augue eget arcu dictum varius duis. Ut enim blandit volutpat
20
+ maecenas volutpat blandit aliquam. Sit amet mauris commodo quis imperdiet massa tincidunt. Habitant morbi
21
+ tristique senectus et netus. Fames ac turpis egestas sed tempus urna. Neque laoreet suspendisse interdum
22
+ consectetur libero id. Volutpat lacus laoreet non curabitur gravida arcu ac tortor. Porta nibh venenatis cras
23
+ sed felis eget velit. Nullam non nisi est sit amet facilisis. Nunc mi ipsum faucibus vitae. Lorem sed risus
24
+ ultricies tristique nulla aliquet enim tortor at. Egestas sed tempus urna et pharetra pharetra massa massa
25
+ ultricies. Lacinia quis vel eros donec ac odio tempor orci. Malesuada fames ac turpis egestas integer eget
26
+ aliquet.
27
+ <br />
28
+ <br />
29
+ Neque aliquam vestibulum morbi blandit cursus risus at ultrices. Molestie at elementum eu facilisis sed odio
30
+ morbi. Elit pellentesque habitant morbi tristique. Consequat nisl vel pretium lectus quam id leo in vitae.
31
+ Quis varius quam quisque id diam vel quam elementum. Viverra nam libero justo laoreet sit amet cursus.
32
+ Sollicitudin tempor id eu nisl nunc. Orci nulla pellentesque dignissim enim sit amet venenatis. Dignissim enim
33
+ sit amet venenatis urna cursus eget. Iaculis at erat pellentesque adipiscing commodo elit. Faucibus pulvinar
34
+ elementum integer enim neque volutpat. Nullam vehicula ipsum a arcu cursus vitae congue mauris. Nunc mattis
35
+ enim ut tellus elementum sagittis vitae. Blandit cursus risus at ultrices. Tellus mauris a diam maecenas sed
36
+ enim. Non diam phasellus vestibulum lorem sed risus ultricies tristique nulla.
37
+ <br />
38
+ <br />
39
+ Nulla pharetra diam sit amet nisl suscipit adipiscing. Ac tortor vitae purus faucibus ornare suspendisse sed
40
+ nisi. Sed felis eget velit aliquet sagittis id consectetur purus. Tincidunt tortor aliquam nulla facilisi cras
41
+ fermentum. Volutpat est velit egestas dui id ornare arcu odio. Pharetra magna ac placerat vestibulum. Ultrices
42
+ sagittis orci a scelerisque purus semper eget duis at. Nisi est sit amet facilisis magna etiam tempor orci eu.
43
+ Convallis tellus id interdum velit. Facilisis sed odio morbi quis commodo odio aenean sed.
44
+ <br />
45
+ <br />
46
+ Eu scelerisque felis imperdiet proin fermentum leo vel orci porta. Facilisi etiam dignissim diam quis enim
47
+ lobortis scelerisque fermentum. Eleifend donec pretium vulputate sapien nec sagittis aliquam malesuada. Magna
48
+ etiam tempor orci eu lobortis elementum. Quis auctor elit sed vulputate mi sit. Eleifend quam adipiscing vitae
49
+ proin sagittis nisl rhoncus mattis rhoncus. Erat velit scelerisque in dictum non. Sit amet nulla facilisi
50
+ morbi tempus iaculis urna. Enim ut tellus elementum sagittis vitae et leo duis ut. Lectus arcu bibendum at
51
+ varius vel pharetra vel turpis. Morbi tristique senectus et netus et. Eget aliquet nibh praesent tristique
52
+ magna sit amet purus gravida. Nisl purus in mollis nunc sed id semper risus. Id neque aliquam vestibulum
53
+ morbi. Mauris a diam maecenas sed enim ut sem. Egestas tellus rutrum tellus pellentesque.
54
+ </ModalBody>
55
+ </ChatbotModal>
56
+ );
57
+ expect(screen.getByTestId('modal')).toHaveClass('pf-m-compact');
58
+ });
59
+ });