Skip to content

Commit 7cece91

Browse files
committed
feat(ChatbotModal): Add style-able modal for general use
1 parent 0019c2e commit 7cece91

File tree

9 files changed

+231
-124
lines changed

9 files changed

+231
-124
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from 'react';
2+
import { Button, ModalBody, ModalFooter, ModalHeader } from '@patternfly/react-core';
3+
import { ChatbotModal } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotModal';
4+
import { ChatbotDisplayMode } from '@patternfly/virtual-assistant/dist/dynamic/Chatbot';
5+
6+
export const ChatbotModalExample: React.FunctionComponent = () => {
7+
const [isModalOpen, setIsModalOpen] = React.useState(false);
8+
9+
const handleModalToggle = (_event: React.MouseEvent | MouseEvent | KeyboardEvent) => {
10+
setIsModalOpen(!isModalOpen);
11+
};
12+
13+
return (
14+
<>
15+
<Button onClick={handleModalToggle}>Launch modal</Button>
16+
<ChatbotModal
17+
isOpen={isModalOpen}
18+
displayMode={ChatbotDisplayMode.default}
19+
onClose={handleModalToggle}
20+
ouiaId="ChatbotModal"
21+
aria-labelledby="basic-modal-title"
22+
aria-describedby="modal-box-body-basic"
23+
>
24+
<ModalHeader title="Basic modal" labelId="basic-modal-title" />
25+
<ModalBody id="modal-box-body-basic">
26+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
27+
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
28+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
29+
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
30+
est laborum.
31+
</ModalBody>
32+
<ModalFooter>
33+
<Button key="confirm" variant="primary" onClick={handleModalToggle}>
34+
Confirm
35+
</Button>
36+
<Button key="cancel" variant="link" onClick={handleModalToggle}>
37+
Cancel
38+
</Button>
39+
</ModalFooter>
40+
</ChatbotModal>
41+
</>
42+
);
43+
};

packages/module/patternfly-docs/content/extensions/virtual-assistant/examples/UI/UI.md

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,33 @@ id: UI
1010
source: react
1111
# If you use typescript, the name of the interface to display props for
1212
# These are found through the sourceProps function provided in patternfly-docs.source.js
13-
propComponents: [
14-
'Chatbot',
15-
'ChatbotContent',
16-
'MessageBox',
17-
'ChatbotWelcomePrompt',
18-
'WelcomePrompt',
19-
'ChatbotToggle',
20-
'ChatbotHeader',
21-
'ChatbotHeaderMain',
22-
'ChatbotHeaderMenu',
23-
'ChatbotHeaderActions',
24-
'ChatbotHeaderTitle',
25-
'ChatbotHeaderOptionsDropdown',
26-
'ChatbotHeaderSelectorDropdown',
27-
'ChatbotFooter',
28-
'MessageBar',
29-
'ChatbotFootnote',
30-
'ChatbotFootnotePopover',
31-
'ChatbotFootnotePopoverCTA',
32-
'ChatbotFootnotePopoverBannerImage',
33-
'ChatbotFootnotePopoverLink',
34-
'MessageBarWithAttachMenuProps',
35-
'SourceDetailsMenuItem',
36-
'ChatbotConversationHistoryNav',
37-
'Conversation'
38-
]
13+
propComponents:
14+
[
15+
'Chatbot',
16+
'ChatbotContent',
17+
'MessageBox',
18+
'ChatbotWelcomePrompt',
19+
'WelcomePrompt',
20+
'ChatbotToggle',
21+
'ChatbotHeader',
22+
'ChatbotHeaderMain',
23+
'ChatbotHeaderMenu',
24+
'ChatbotHeaderActions',
25+
'ChatbotHeaderTitle',
26+
'ChatbotHeaderOptionsDropdown',
27+
'ChatbotHeaderSelectorDropdown',
28+
'ChatbotFooter',
29+
'MessageBar',
30+
'ChatbotFootnote',
31+
'ChatbotFootnotePopover',
32+
'ChatbotFootnotePopoverCTA',
33+
'ChatbotFootnotePopoverBannerImage',
34+
'ChatbotFootnotePopoverLink',
35+
'MessageBarWithAttachMenuProps',
36+
'SourceDetailsMenuItem',
37+
'ChatbotConversationHistoryNav',
38+
'Conversation'
39+
]
3940
sortValue: 2
4041
---
4142

@@ -63,6 +64,7 @@ ChatbotHeaderSelectorDropdown
6364
import { ChatbotFooter, ChatbotFootnote } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotFooter';
6465
import { MessageBar } from '@patternfly/virtual-assistant/dist/dynamic/MessageBar';
6566
import SourceDetailsMenuItem from '@patternfly/virtual-assistant/dist/dynamic/SourceDetailsMenuItem';
67+
import { ChatbotModal } from '@patternfly/virtual-assistant/dist/dynamic/ChatbotModal';
6668
import { BellIcon, CalendarAltIcon, ClipboardIcon, CodeIcon, UploadIcon } from '@patternfly/react-icons';
6769
import { useDropzone } from 'react-dropzone';
6870

@@ -265,6 +267,7 @@ To enable the stop button, set `hasStopButton` to `true` and pass in a `handleSt
265267
## Navigation
266268

267269
### Side nav in a drawer
270+
268271
The chatbot conversation history is contained in an interactive drawer, where users can interact with previous conversations or start a new conversation.
269272

270273
The `<ChatbotConversationHistoryNav>` component is a wrapper placed within `<Chatbot>`, 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
313316
```js file="./ChatbotHeaderDrawerWithActions.tsx"
314317

315318
```
319+
320+
### Modal
321+
322+
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](/patternfly-ai/chatbot/messages#attachment-preview), but you can customize this modal to adapt it to other use cases as needed.
323+
324+
```js file="./ChatbotModal.tsx"
325+
326+
```
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
.pf-chatbot__chatbot-modal-backdrop {
2+
position: static;
3+
}
4+
5+
.pf-chatbot__chatbot-modal {
6+
--pf-v6-c-modal-box--BorderRadius: var(--pf-t--global--border--radius--medium);
7+
position: fixed;
8+
inset-block-end: var(--pf-t--global--spacer--800); // no associated semantic token
9+
inset-inline-end: var(--pf-t--global--spacer--lg);
10+
width: 30rem;
11+
height: 70vh;
12+
background-color: var(--pf-t--global--background--color--secondary--default);
13+
14+
.pf-v6-c-modal-box__title {
15+
--pf-v6-c-modal-box__title--FontSize: var(--pf-t--global--font--size--heading--h3);
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: 500;
20+
}
21+
.pf-v6-c-modal-box__footer {
22+
padding-block-start: var(--pf-t--global--spacer--xl);
23+
padding-block-end: var(--pf-t--global--spacer--xl);
24+
}
25+
.pf-v6-c-modal-box__header {
26+
padding-block-end: var(--pf-t--global--spacer--lg);
27+
}
28+
}
29+
30+
// ============================================================================
31+
// Chatbot Display Mode - Fullscreen and Embedded
32+
// ============================================================================
33+
@media screen and (max-width: 600px) {
34+
.pf-chatbot__chatbot-modal--embedded,
35+
.pf-chatbot__chatbot-modal--fullscreen {
36+
inset-block-end: 0;
37+
inset-inline-end: 0;
38+
width: 100%;
39+
height: 100%;
40+
top: 50%;
41+
left: 50%;
42+
transform: translate(-50%, -50%);
43+
}
44+
}
45+
@media screen and (min-width: 601px) {
46+
.pf-chatbot__chatbot-modal--embedded,
47+
.pf-chatbot__chatbot-modal--fullscreen {
48+
inset-block-end: 0;
49+
inset-inline-end: 0;
50+
width: 50%;
51+
height: fit-content;
52+
top: 50%;
53+
left: 50%;
54+
transform: translate(-50%, -50%);
55+
}
56+
}
57+
58+
// ============================================================================
59+
// Chatbot Display Mode - Default
60+
// ============================================================================
61+
.pf-chatbot__chatbot-modal--default {
62+
box-shadow: unset;
63+
}
64+
65+
// ============================================================================
66+
// Chatbot Display Mode - Docked
67+
// ============================================================================
68+
.pf-chatbot__chatbot-modal--docked {
69+
height: 100vh;
70+
inset-block-end: 0;
71+
inset-inline-end: 0;
72+
border-radius: 0;
73+
--pf-v6-c-modal-box--MaxHeight: 100vh;
74+
box-shadow: unset;
75+
}
76+
77+
// ============================================================================
78+
// Dark theme
79+
// ============================================================================
80+
.pf-v6-theme-dark {
81+
.pf-v6-c-modal-box.pf-chatbot__chatbot-modal {
82+
.pf-v6-c-modal-box__title {
83+
color: #fff;
84+
}
85+
}
86+
}
87+
88+
// ============================================================================
89+
// Backdrop
90+
// ============================================================================
91+
.pf-v6-c-backdrop.pf-chatbot__backdrop {
92+
position: absolute;
93+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// ============================================================================
2+
// Code Modal - Chatbot Modal with Code Editor
3+
// ============================================================================
4+
import React from 'react';
5+
6+
// Import PatternFly components
7+
import { Modal, ModalProps } from '@patternfly/react-core';
8+
import { ChatbotDisplayMode } from '../Chatbot';
9+
10+
export interface ChatbotModalProps extends Omit<ModalProps, 'ref'> {
11+
/** Display mode for the Chatbot parent; this influences the styles applied */
12+
displayMode?: ChatbotDisplayMode;
13+
className?: string;
14+
}
15+
16+
export const ChatbotModal: React.FunctionComponent<ChatbotModalProps> = ({
17+
children,
18+
displayMode = ChatbotDisplayMode.default,
19+
className,
20+
isOpen,
21+
...props
22+
}: ChatbotModalProps) => {
23+
const modal = (
24+
<Modal
25+
isOpen={isOpen}
26+
ouiaId="ChatbotModal"
27+
aria-labelledby="chatbot-modal-title"
28+
aria-describedby="chatbot-modal"
29+
className={`pf-chatbot__chatbot-modal pf-chatbot__chatbot-modal--${displayMode} ${className}`}
30+
backdropClassName="pf-chatbot__chatbot-modal-backdrop"
31+
{...props}
32+
>
33+
{children}
34+
</Modal>
35+
);
36+
37+
if ((displayMode === ChatbotDisplayMode.fullscreen || displayMode === ChatbotDisplayMode.embedded) && isOpen) {
38+
return <div className="pf-v6-c-backdrop pf-chatbot__backdrop">{modal}</div>;
39+
}
40+
return modal;
41+
};
42+
43+
export default ChatbotModal;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { default } from './ChatbotModal';
2+
3+
export * from './ChatbotModal';
Lines changed: 3 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,4 @@
1-
.pf-chatbot__code-modal-backdrop {
2-
position: static;
3-
}
4-
51
.pf-chatbot__code-modal {
6-
--pf-v6-c-modal-box--BorderRadius: var(--pf-t--global--border--radius--medium);
7-
position: fixed;
8-
inset-block-end: var(--pf-t--global--spacer--800); // no associated semantic token
9-
inset-inline-end: var(--pf-t--global--spacer--lg);
10-
width: 30rem;
11-
height: 70vh;
12-
background-color: var(--pf-t--global--background--color--secondary--default);
13-
14-
.pf-v6-c-modal-box__title {
15-
--pf-v6-c-modal-box__title--FontSize: var(--pf-t--global--font--size--heading--h3);
16-
}
172
.pf-v6-c-code-editor {
183
--pf-v6-c-code-editor__main--BackgroundColor: #1f1f1f;
194
--pf-v6-c-code-editor__main--BorderEndStartRadius: 0;
@@ -26,6 +11,9 @@
2611
border-start-start-radius: var(--pf-t--global--border--radius--small);
2712
border-start-end-radius: var(--pf-t--global--border--radius--small);
2813
}
14+
.pf-chatbot__code-modal-body {
15+
gap: var(--pf-t--global--spacer--lg);
16+
}
2917
.pf-chatbot__code-modal--controls > .pf-v6-c-code-editor__header {
3018
flex-direction: row-reverse;
3119
border-radius: var(--pf-t--global--border--radius--small);
@@ -70,75 +58,11 @@
7058
.pf-v6-c-code-editor__header-main {
7159
display: none;
7260
}
73-
.pf-v6-c-button.pf-m-primary.pf-m-block,
74-
.pf-v6-c-button.pf-m-link.pf-m-block {
75-
--pf-v6-c-button--FontWeight: 500;
76-
}
77-
.pf-v6-c-modal-box__close {
78-
--pf-v6-c-modal-box__close--InsetBlockStart: 1.125rem;
79-
}
80-
.pf-chatbot__code-modal-body {
81-
gap: var(--pf-t--global--spacer--lg);
82-
}
83-
.pf-v6-c-modal-box__footer {
84-
padding-block-start: var(--pf-t--global--spacer--xl);
85-
padding-block-end: var(--pf-t--global--spacer--xl);
86-
}
87-
.pf-v6-c-modal-box__header {
88-
padding-block-end: var(--pf-t--global--spacer--lg);
89-
}
9061
.pf-chatbot__code-modal-file-details {
9162
padding-inline-start: var(--pf-t--global--spacer--md);
9263
}
9364
}
9465

95-
// ============================================================================
96-
// Chatbot Display Mode - Fullscreen and Embedded
97-
// ============================================================================
98-
@media screen and (max-width: 600px) {
99-
.pf-chatbot__code-modal--embedded,
100-
.pf-chatbot__code-modal--fullscreen {
101-
inset-block-end: 0;
102-
inset-inline-end: 0;
103-
width: 100%;
104-
height: 100%;
105-
top: 50%;
106-
left: 50%;
107-
transform: translate(-50%, -50%);
108-
}
109-
}
110-
@media screen and (min-width: 601px) {
111-
.pf-chatbot__code-modal--embedded,
112-
.pf-chatbot__code-modal--fullscreen {
113-
inset-block-end: 0;
114-
inset-inline-end: 0;
115-
width: 50%;
116-
height: fit-content;
117-
top: 50%;
118-
left: 50%;
119-
transform: translate(-50%, -50%);
120-
}
121-
}
122-
123-
// ============================================================================
124-
// Chatbot Display Mode - Default
125-
// ============================================================================
126-
.pf-chatbot__code-modal--default {
127-
box-shadow: unset;
128-
}
129-
130-
// ============================================================================
131-
// Chatbot Display Mode - Docked
132-
// ============================================================================
133-
.pf-chatbot__code-modal--docked {
134-
height: 100vh;
135-
inset-block-end: 0;
136-
inset-inline-end: 0;
137-
border-radius: 0;
138-
--pf-v6-c-modal-box--MaxHeight: 100vh;
139-
box-shadow: unset;
140-
}
141-
14266
// ============================================================================
14367
// Dark theme
14468
// ============================================================================
@@ -149,16 +73,4 @@
14973
--pf-v6-c-button--hover__icon--Color: #c7c7c7;
15074
}
15175
}
152-
.pf-v6-c-modal-box.pf-chatbot__code-modal {
153-
.pf-v6-c-modal-box__title {
154-
color: #fff;
155-
}
156-
}
157-
}
158-
159-
// ============================================================================
160-
// Backdrop
161-
// ============================================================================
162-
.pf-v6-c-backdrop.pf-chatbot__backdrop {
163-
position: absolute;
16476
}

0 commit comments

Comments
 (0)