diff --git a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/ChatbotModal.tsx b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/ChatbotModal.tsx
new file mode 100644
index 00000000..9769a8fc
--- /dev/null
+++ b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/ChatbotModal.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { Button, FormGroup, ModalBody, ModalFooter, ModalHeader, Radio } from '@patternfly/react-core';
+import { ChatbotModal } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotModal';
+import Chatbot, { ChatbotDisplayMode } from '@patternfly/virtual-assistant/dist/dynamic/Chatbot';
+
+export const ChatbotModalExample: React.FunctionComponent = () => {
+ const [isModalOpen, setIsModalOpen] = React.useState(false);
+ const [displayMode, setDisplayMode] = React.useState(ChatbotDisplayMode.default);
+
+ const handleModalToggle = (_event: React.MouseEvent | MouseEvent | KeyboardEvent) => {
+ setIsModalOpen(!isModalOpen);
+ };
+
+ return (
+ <>
+
+
+ setDisplayMode(ChatbotDisplayMode.default)}
+ name="basic-inline-radio"
+ label="Default"
+ id="default"
+ />
+ setDisplayMode(ChatbotDisplayMode.docked)}
+ name="basic-inline-radio"
+ label="Docked"
+ id="docked"
+ />
+ setDisplayMode(ChatbotDisplayMode.fullscreen)}
+ name="basic-inline-radio"
+ label="Fullscreen"
+ id="fullscreen"
+ />
+ setDisplayMode(ChatbotDisplayMode.embedded)}
+ name="basic-inline-radio"
+ label="Embedded"
+ id="embedded"
+ />
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
+ magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+ pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
+ est laborum.
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/UI.md b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/UI.md
index 179f6566..3c35caa4 100644
--- a/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/UI.md
+++ b/packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/UI.md
@@ -10,32 +10,33 @@ id: UI
source: react
# If you use typescript, the name of the interface to display props for
# These are found through the sourceProps function provided in patternfly-docs.source.js
-propComponents: [
- 'Chatbot',
- 'ChatbotContent',
- 'MessageBox',
- 'ChatbotWelcomePrompt',
- 'WelcomePrompt',
- 'ChatbotToggle',
- 'ChatbotHeader',
- 'ChatbotHeaderMain',
- 'ChatbotHeaderMenu',
- 'ChatbotHeaderActions',
- 'ChatbotHeaderTitle',
- 'ChatbotHeaderOptionsDropdown',
- 'ChatbotHeaderSelectorDropdown',
- 'ChatbotFooter',
- 'MessageBar',
- 'ChatbotFootnote',
- 'ChatbotFootnotePopover',
- 'ChatbotFootnotePopoverCTA',
- 'ChatbotFootnotePopoverBannerImage',
- 'ChatbotFootnotePopoverLink',
- 'MessageBarWithAttachMenuProps',
- 'SourceDetailsMenuItem',
- 'ChatbotConversationHistoryNav',
- 'Conversation'
-]
+propComponents:
+ [
+ 'Chatbot',
+ 'ChatbotContent',
+ 'MessageBox',
+ 'ChatbotWelcomePrompt',
+ 'WelcomePrompt',
+ 'ChatbotToggle',
+ 'ChatbotHeader',
+ 'ChatbotHeaderMain',
+ 'ChatbotHeaderMenu',
+ 'ChatbotHeaderActions',
+ 'ChatbotHeaderTitle',
+ 'ChatbotHeaderOptionsDropdown',
+ 'ChatbotHeaderSelectorDropdown',
+ 'ChatbotFooter',
+ 'MessageBar',
+ 'ChatbotFootnote',
+ 'ChatbotFootnotePopover',
+ 'ChatbotFootnotePopoverCTA',
+ 'ChatbotFootnotePopoverBannerImage',
+ 'ChatbotFootnotePopoverLink',
+ 'MessageBarWithAttachMenuProps',
+ 'SourceDetailsMenuItem',
+ 'ChatbotConversationHistoryNav',
+ 'Conversation'
+ ]
sortValue: 2
---
@@ -63,6 +64,7 @@ ChatbotHeaderSelectorDropdown
import { ChatbotFooter, ChatbotFootnote } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotFooter';
import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar';
import SourceDetailsMenuItem from '@patternfly/virtual-assistant/dist/dynamic/SourceDetailsMenuItem';
+import { ChatbotModal } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotModal';
import { BellIcon, CalendarAltIcon, ClipboardIcon, CodeIcon, UploadIcon } from '@patternfly/react-icons';
import { useDropzone } from 'react-dropzone';
@@ -265,6 +267,7 @@ To enable the stop button, set `hasStopButton` to `true` and pass in a `handleSt
## Navigation
### Side nav in a drawer
+
The chatbot conversation history is contained in an interactive drawer, where users can interact with previous conversations or start a new conversation.
The `` component is a wrapper placed within ``, which contains all other chatbot components in `drawerContent`. There is a focus trap so users can only tab within the drawer while it is open.
@@ -313,3 +316,11 @@ Actions can be added to conversations with `menuItems`. Optionally, you can also
```js file="./ChatbotHeaderDrawerWithActions.tsx"
```
+
+### Modal
+
+Based on the [PatternFly modal](/components/modal), this modal adapts to the chatbot display mode and accepts components typically used in a modal. It is primarily used and tested in the context of the attachment modals, but you can customize this modal to adapt it to other use cases as needed. The modal will overlay the chatbot in default and docked modes, and will behave more like a traditional PatternFly modal in fullscreen and embedded modes.
+
+```js file="./ChatbotModal.tsx" isFullscreen
+
+```
diff --git a/packages/module/src/ChatbotModal/ChatbotModal.scss b/packages/module/src/ChatbotModal/ChatbotModal.scss
new file mode 100644
index 00000000..5d561cbb
--- /dev/null
+++ b/packages/module/src/ChatbotModal/ChatbotModal.scss
@@ -0,0 +1,93 @@
+.pf-chatbot__chatbot-modal-backdrop {
+ position: static;
+}
+
+.pf-chatbot__chatbot-modal {
+ --pf-v6-c-modal-box--BorderRadius: var(--pf-t--global--border--radius--medium);
+ position: fixed;
+ inset-block-end: var(--pf-t--global--spacer--800); // no associated semantic token
+ inset-inline-end: var(--pf-t--global--spacer--lg);
+ width: 30rem;
+ height: 70vh;
+ background-color: var(--pf-t--global--background--color--secondary--default);
+
+ .pf-v6-c-modal-box__title {
+ --pf-v6-c-modal-box__title--FontSize: var(--pf-t--global--font--size--heading--h3);
+ }
+ .pf-v6-c-button.pf-m-primary.pf-m-block,
+ .pf-v6-c-button.pf-m-link.pf-m-block {
+ --pf-v6-c-button--FontWeight: 500;
+ }
+ .pf-v6-c-modal-box__footer {
+ padding-block-start: var(--pf-t--global--spacer--xl);
+ padding-block-end: var(--pf-t--global--spacer--xl);
+ }
+ .pf-v6-c-modal-box__header {
+ padding-block-end: var(--pf-t--global--spacer--lg);
+ }
+}
+
+// ============================================================================
+// Chatbot Display Mode - Fullscreen and Embedded
+// ============================================================================
+@media screen and (max-width: 600px) {
+ .pf-chatbot__chatbot-modal--embedded,
+ .pf-chatbot__chatbot-modal--fullscreen {
+ inset-block-end: 0;
+ inset-inline-end: 0;
+ width: 100%;
+ height: 100%;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+}
+@media screen and (min-width: 601px) {
+ .pf-chatbot__chatbot-modal--embedded,
+ .pf-chatbot__chatbot-modal--fullscreen {
+ inset-block-end: 0;
+ inset-inline-end: 0;
+ width: 50%;
+ height: fit-content;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+}
+
+// ============================================================================
+// Chatbot Display Mode - Default
+// ============================================================================
+.pf-chatbot__chatbot-modal--default {
+ box-shadow: unset;
+}
+
+// ============================================================================
+// Chatbot Display Mode - Docked
+// ============================================================================
+.pf-chatbot__chatbot-modal--docked {
+ height: 100vh;
+ inset-block-end: 0;
+ inset-inline-end: 0;
+ border-radius: 0;
+ --pf-v6-c-modal-box--MaxHeight: 100vh;
+ box-shadow: unset;
+}
+
+// ============================================================================
+// Dark theme
+// ============================================================================
+.pf-v6-theme-dark {
+ .pf-v6-c-modal-box.pf-chatbot__chatbot-modal {
+ .pf-v6-c-modal-box__title {
+ color: #fff;
+ }
+ }
+}
+
+// ============================================================================
+// Backdrop
+// ============================================================================
+.pf-v6-c-backdrop.pf-chatbot__backdrop {
+ position: absolute;
+}
diff --git a/packages/module/src/ChatbotModal/ChatbotModal.tsx b/packages/module/src/ChatbotModal/ChatbotModal.tsx
new file mode 100644
index 00000000..28c1450b
--- /dev/null
+++ b/packages/module/src/ChatbotModal/ChatbotModal.tsx
@@ -0,0 +1,43 @@
+// ============================================================================
+// Code Modal - Chatbot Modal with Code Editor
+// ============================================================================
+import React from 'react';
+
+// Import PatternFly components
+import { Modal, ModalProps } from '@patternfly/react-core';
+import { ChatbotDisplayMode } from '../Chatbot';
+
+export interface ChatbotModalProps extends Omit {
+ /** Display mode for the Chatbot parent; this influences the styles applied */
+ displayMode?: ChatbotDisplayMode;
+ className?: string;
+}
+
+export const ChatbotModal: React.FunctionComponent = ({
+ children,
+ displayMode = ChatbotDisplayMode.default,
+ className,
+ isOpen,
+ ...props
+}: ChatbotModalProps) => {
+ const modal = (
+
+ {children}
+
+ );
+
+ if ((displayMode === ChatbotDisplayMode.fullscreen || displayMode === ChatbotDisplayMode.embedded) && isOpen) {
+ return {modal}
;
+ }
+ return modal;
+};
+
+export default ChatbotModal;
diff --git a/packages/module/src/ChatbotModal/index.ts b/packages/module/src/ChatbotModal/index.ts
new file mode 100644
index 00000000..fab93022
--- /dev/null
+++ b/packages/module/src/ChatbotModal/index.ts
@@ -0,0 +1,3 @@
+export { default } from './ChatbotModal';
+
+export * from './ChatbotModal';
diff --git a/packages/module/src/CodeModal/CodeModal.scss b/packages/module/src/CodeModal/CodeModal.scss
index 0b2a93e1..8598921c 100644
--- a/packages/module/src/CodeModal/CodeModal.scss
+++ b/packages/module/src/CodeModal/CodeModal.scss
@@ -1,19 +1,4 @@
-.pf-chatbot__code-modal-backdrop {
- position: static;
-}
-
.pf-chatbot__code-modal {
- --pf-v6-c-modal-box--BorderRadius: var(--pf-t--global--border--radius--medium);
- position: fixed;
- inset-block-end: var(--pf-t--global--spacer--800); // no associated semantic token
- inset-inline-end: var(--pf-t--global--spacer--lg);
- width: 30rem;
- height: 70vh;
- background-color: var(--pf-t--global--background--color--secondary--default);
-
- .pf-v6-c-modal-box__title {
- --pf-v6-c-modal-box__title--FontSize: var(--pf-t--global--font--size--heading--h3);
- }
.pf-v6-c-code-editor {
--pf-v6-c-code-editor__main--BackgroundColor: #1f1f1f;
--pf-v6-c-code-editor__main--BorderEndStartRadius: 0;
@@ -26,6 +11,9 @@
border-start-start-radius: var(--pf-t--global--border--radius--small);
border-start-end-radius: var(--pf-t--global--border--radius--small);
}
+ .pf-chatbot__code-modal-body {
+ gap: var(--pf-t--global--spacer--lg);
+ }
.pf-chatbot__code-modal--controls > .pf-v6-c-code-editor__header {
flex-direction: row-reverse;
border-radius: var(--pf-t--global--border--radius--small);
@@ -70,75 +58,11 @@
.pf-v6-c-code-editor__header-main {
display: none;
}
- .pf-v6-c-button.pf-m-primary.pf-m-block,
- .pf-v6-c-button.pf-m-link.pf-m-block {
- --pf-v6-c-button--FontWeight: 500;
- }
- .pf-v6-c-modal-box__close {
- --pf-v6-c-modal-box__close--InsetBlockStart: 1.125rem;
- }
- .pf-chatbot__code-modal-body {
- gap: var(--pf-t--global--spacer--lg);
- }
- .pf-v6-c-modal-box__footer {
- padding-block-start: var(--pf-t--global--spacer--xl);
- padding-block-end: var(--pf-t--global--spacer--xl);
- }
- .pf-v6-c-modal-box__header {
- padding-block-end: var(--pf-t--global--spacer--lg);
- }
.pf-chatbot__code-modal-file-details {
padding-inline-start: var(--pf-t--global--spacer--md);
}
}
-// ============================================================================
-// Chatbot Display Mode - Fullscreen and Embedded
-// ============================================================================
-@media screen and (max-width: 600px) {
- .pf-chatbot__code-modal--embedded,
- .pf-chatbot__code-modal--fullscreen {
- inset-block-end: 0;
- inset-inline-end: 0;
- width: 100%;
- height: 100%;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
-}
-@media screen and (min-width: 601px) {
- .pf-chatbot__code-modal--embedded,
- .pf-chatbot__code-modal--fullscreen {
- inset-block-end: 0;
- inset-inline-end: 0;
- width: 50%;
- height: fit-content;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
-}
-
-// ============================================================================
-// Chatbot Display Mode - Default
-// ============================================================================
-.pf-chatbot__code-modal--default {
- box-shadow: unset;
-}
-
-// ============================================================================
-// Chatbot Display Mode - Docked
-// ============================================================================
-.pf-chatbot__code-modal--docked {
- height: 100vh;
- inset-block-end: 0;
- inset-inline-end: 0;
- border-radius: 0;
- --pf-v6-c-modal-box--MaxHeight: 100vh;
- box-shadow: unset;
-}
-
// ============================================================================
// Dark theme
// ============================================================================
@@ -149,16 +73,4 @@
--pf-v6-c-button--hover__icon--Color: #c7c7c7;
}
}
- .pf-v6-c-modal-box.pf-chatbot__code-modal {
- .pf-v6-c-modal-box__title {
- color: #fff;
- }
- }
-}
-
-// ============================================================================
-// Backdrop
-// ============================================================================
-.pf-v6-c-backdrop.pf-chatbot__backdrop {
- position: absolute;
}
diff --git a/packages/module/src/CodeModal/CodeModal.tsx b/packages/module/src/CodeModal/CodeModal.tsx
index f3b30984..09844471 100644
--- a/packages/module/src/CodeModal/CodeModal.tsx
+++ b/packages/module/src/CodeModal/CodeModal.tsx
@@ -6,9 +6,10 @@ import path from 'path-browserify';
// Import PatternFly components
import { CodeEditor } from '@patternfly/react-code-editor';
-import { Button, Modal, ModalBody, ModalFooter, ModalHeader, Stack, StackItem } from '@patternfly/react-core';
+import { Button, ModalBody, ModalFooter, ModalHeader, Stack, StackItem } from '@patternfly/react-core';
import FileDetails, { extensionToLanguage } from '../FileDetails';
import { ChatbotDisplayMode } from '../Chatbot';
+import ChatbotModal from '../ChatbotModal/ChatbotModal';
export interface CodeModalProps {
/** Class applied to code editor */
@@ -98,14 +99,14 @@ export const CodeModal: React.FunctionComponent = ({
/* eslint-enable indent */
const modal = (
-
@@ -143,12 +144,9 @@ export const CodeModal: React.FunctionComponent = ({
{secondaryActionBtn}
-
+
);
- if ((displayMode === ChatbotDisplayMode.fullscreen || displayMode === ChatbotDisplayMode.embedded) && isModalOpen) {
- return {modal}
;
- }
return modal;
};
diff --git a/packages/module/src/index.ts b/packages/module/src/index.ts
index b1bfb770..7b599e2c 100644
--- a/packages/module/src/index.ts
+++ b/packages/module/src/index.ts
@@ -24,6 +24,9 @@ export * from './ChatbotFooter';
export { default as ChatbotHeader } from './ChatbotHeader';
export * from './ChatbotHeader';
+export { default as ChatbotModal } from './ChatbotModal';
+export * from './ChatbotModal';
+
export { default as ChatbotPopover } from './ChatbotPopover';
export * from './ChatbotPopover';
diff --git a/packages/module/src/main.scss b/packages/module/src/main.scss
index bb4c8d6c..c11add72 100644
--- a/packages/module/src/main.scss
+++ b/packages/module/src/main.scss
@@ -5,6 +5,7 @@
@import './ChatbotConversationHistoryNav/ChatbotConversationHistoryNav';
@import './ChatbotFooter/ChatbotFooter';
@import './ChatbotHeader/ChatbotHeader';
+@import './ChatbotModal/ChatbotModal';
@import './ChatbotPopover/ChatbotPopover';
@import './ChatbotToggle/ChatbotToggle';
@import './ChatbotWelcomePrompt/ChatbotWelcomePrompt';