@patternfly/chatbot 6.5.0-prerelease.17 → 6.5.0-prerelease.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +29 -2
- package/dist/cjs/CodeModal/CodeModal.d.ts +2 -0
- package/dist/cjs/CodeModal/CodeModal.js +53 -12
- package/dist/cjs/Onboarding/Onboarding.d.ts +36 -0
- package/dist/cjs/Onboarding/Onboarding.js +37 -0
- package/dist/cjs/Onboarding/Onboarding.test.d.ts +1 -0
- package/dist/cjs/Onboarding/Onboarding.test.js +80 -0
- package/dist/cjs/Onboarding/index.d.ts +2 -0
- package/dist/cjs/Onboarding/index.js +23 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +4 -1
- package/dist/css/main.css +83 -0
- package/dist/css/main.css.map +1 -1
- package/dist/dynamic/Onboarding/package.json +1 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +30 -3
- package/dist/esm/CodeModal/CodeModal.d.ts +2 -0
- package/dist/esm/CodeModal/CodeModal.js +54 -13
- package/dist/esm/Onboarding/Onboarding.d.ts +36 -0
- package/dist/esm/Onboarding/Onboarding.js +30 -0
- package/dist/esm/Onboarding/Onboarding.test.d.ts +1 -0
- package/dist/esm/Onboarding/Onboarding.test.js +75 -0
- package/dist/esm/Onboarding/index.d.ts +2 -0
- package/dist/esm/Onboarding/index.js +2 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -3
- package/patternfly-docs/content/extensions/chatbot/about-chatbot.md +3 -3
- package/patternfly-docs/content/extensions/chatbot/design-guidelines.md +3 -3
- package/patternfly-docs/content/extensions/chatbot/examples/Analytics/Analytics.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Customizing Messages/Customizing Messages.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickResponses.tsx +11 -0
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/UI/CompactOnboarding.tsx +141 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/Onboarding.tsx +151 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/RH-Hat-Image.svg +9 -0
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +38 -20
- package/patternfly-docs/content/extensions/chatbot/examples/demos/AttachmentDemos.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +2 -2
- package/patternfly-docs/patternfly-docs.config.js +1 -1
- package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +56 -14
- package/src/ChatbotModal/ChatbotModal.scss +3 -0
- package/src/CodeModal/CodeModal.tsx +71 -26
- package/src/Onboarding/Onboarding.scss +101 -0
- package/src/Onboarding/Onboarding.test.tsx +148 -0
- package/src/Onboarding/Onboarding.tsx +126 -0
- package/src/Onboarding/index.ts +3 -0
- package/src/index.ts +3 -0
- package/src/main.scss +1 -0
- package/tsconfig.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Sidenav top-level section
|
|
3
3
|
# should be the same for all markdown files
|
|
4
|
-
section:
|
|
4
|
+
section: extensions
|
|
5
5
|
subsection: ChatBot
|
|
6
6
|
# Sidenav secondary level section
|
|
7
7
|
# should be the same for all markdown files
|
|
@@ -53,6 +53,7 @@ import FileDropZone from '@patternfly/chatbot/dist/dynamic/FileDropZone';
|
|
|
53
53
|
import { PreviewAttachment } from '@patternfly/chatbot/dist/dynamic/PreviewAttachment';
|
|
54
54
|
import ChatbotAlert from '@patternfly/chatbot/dist/dynamic/ChatbotAlert';
|
|
55
55
|
import TermsOfUse from '@patternfly/chatbot/dist/dynamic/TermsOfUse';
|
|
56
|
+
import Onboarding from '@patternfly/chatbot/dist/dynamic/Onboarding';
|
|
56
57
|
import {
|
|
57
58
|
ChatbotHeader,
|
|
58
59
|
ChatbotHeaderCloseButton,
|
|
@@ -85,6 +86,7 @@ import PFHorizontalLogoReverse from './PF-HorizontalLogo-Reverse.svg';
|
|
|
85
86
|
import userAvatar from '../Messages/user_avatar.svg';
|
|
86
87
|
import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
|
|
87
88
|
import termsAndConditionsHeader from './PF-TermsAndConditionsHeader.svg';
|
|
89
|
+
import onboardingHeader from './RH-Hat-Image.svg';
|
|
88
90
|
import { CloseIcon, SearchIcon, OutlinedCommentsIcon } from '@patternfly/react-icons';
|
|
89
91
|
import { FunctionComponent, FormEvent, useState, useRef, MouseEvent, isValidElement, cloneElement, Children, ReactNode, Ref, MouseEvent as ReactMouseEvent, CSSProperties, useEffect} from 'react';
|
|
90
92
|
import FilePreview from '@patternfly/chatbot/dist/dynamic/FilePreview';
|
|
@@ -127,7 +129,7 @@ Your code structure should look like this:
|
|
|
127
129
|
|
|
128
130
|
### Welcome message
|
|
129
131
|
|
|
130
|
-
To introduce users to the ChatBot experience, display a welcome message before they input their first message. This brief message should follow our [conversation design guidelines](/
|
|
132
|
+
To introduce users to the ChatBot experience, display a welcome message before they input their first message. This brief message should follow our [conversation design guidelines](/ai/conversation-design) to welcome users to the ChatBot experience and encourage them to interact.
|
|
131
133
|
|
|
132
134
|
This message can be dismissed once a user sends their first message. To change the arrangement of the message within the message box, specify the `position` in the `<MessageBox>` component.
|
|
133
135
|
|
|
@@ -424,24 +426,6 @@ The drawer can also be used to display a list of basic menu items.
|
|
|
424
426
|
|
|
425
427
|
```
|
|
426
428
|
|
|
427
|
-
### Terms of use
|
|
428
|
-
|
|
429
|
-
Based on the [PatternFly modal](/components/modal), this modal adapts to the ChatBot display mode and is meant to display terms and conditions for using a ChatBot in your project. The image in the header can be toggled on or off depending on whether the `image` and `altText` props are provided.
|
|
430
|
-
|
|
431
|
-
This example also includes an example of how to use [skip to content](/extensions/chatbot/ui#skip-to-content). When the terms of use modal is open, focus is placed on the terms of use container. When it is closed, focus is placed on the ChatBot. In a real example with a functioning ChatBot toggle, you would also want to place focus on the toggle when appropriate.
|
|
432
|
-
|
|
433
|
-
```js file="./TermsOfUse.tsx" isFullscreen
|
|
434
|
-
|
|
435
|
-
```
|
|
436
|
-
|
|
437
|
-
### Compact terms of use
|
|
438
|
-
|
|
439
|
-
To apply compact styling to the terms of use modal, pass `isCompact` to `<TermsOfUse>`. This will remove the header image and adjust the spacing of text, so that there is less white space in the modal.
|
|
440
|
-
|
|
441
|
-
```js file="./TermsOfUseCompact.tsx" isFullscreen
|
|
442
|
-
|
|
443
|
-
```
|
|
444
|
-
|
|
445
429
|
### Settings
|
|
446
430
|
|
|
447
431
|
To contain user preference controls and other ChatBot setting options, you can create a separate settings page that can accept any number of buttons, dropdown menus, toggles, labels, and so on. This settings page will render all components appropriately within all 4 display modes.
|
|
@@ -469,3 +453,37 @@ Based on the [PatternFly modal](/components/modal), this modal adapts to the Cha
|
|
|
469
453
|
```js file="./ChatbotModal.tsx" isFullscreen
|
|
470
454
|
|
|
471
455
|
```
|
|
456
|
+
|
|
457
|
+
### Onboarding
|
|
458
|
+
|
|
459
|
+
You can use the onboarding modal to introduce users to your ChatBot and provide necessary information. The title, image, and body text are customizable.
|
|
460
|
+
|
|
461
|
+
```js file="./Onboarding.tsx" isFullscreen
|
|
462
|
+
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Compact onboarding
|
|
466
|
+
|
|
467
|
+
To make the onboarding modal compact, with less spacing, pass `isCompact` to the `<Onboarding>` component.
|
|
468
|
+
|
|
469
|
+
```js file="./CompactOnboarding.tsx" isFullscreen
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### Terms of use
|
|
474
|
+
|
|
475
|
+
Based on the [PatternFly modal](/components/modal), this modal adapts to the ChatBot display mode and is meant to display terms and conditions for using a ChatBot in your project. The image in the header can be toggled on or off depending on whether the `image` and `altText` props are provided.
|
|
476
|
+
|
|
477
|
+
This example also includes an example of how to use [skip to content](/extensions/chatbot/ui#skip-to-content). When the terms of use modal is open, focus is placed on the terms of use container. When it is closed, focus is placed on the ChatBot. In a real example with a functioning ChatBot toggle, you would also want to place focus on the toggle when appropriate.
|
|
478
|
+
|
|
479
|
+
```js file="./TermsOfUse.tsx" isFullscreen
|
|
480
|
+
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Compact terms of use
|
|
484
|
+
|
|
485
|
+
To apply compact styling to the terms of use modal, pass `isCompact` to `<TermsOfUse>`. This will remove the header image and adjust the spacing of text, so that there is less white space in the modal.
|
|
486
|
+
|
|
487
|
+
```js file="./TermsOfUseCompact.tsx" isFullscreen
|
|
488
|
+
|
|
489
|
+
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
# Sidenav top-level section
|
|
3
3
|
# should be the same for all markdown files
|
|
4
|
-
section:
|
|
4
|
+
section: extensions
|
|
5
5
|
subsection: ChatBot
|
|
6
6
|
# Sidenav secondary level section
|
|
7
7
|
# should be the same for all markdown files
|
|
@@ -133,7 +133,7 @@ This demo displays a ChatBot in a static, inline drawer. This demo includes:
|
|
|
133
133
|
|
|
134
134
|
### Primary color background
|
|
135
135
|
|
|
136
|
-
This demo displays an embedded ChatBot with a [primary background color](/design-foundations/colors#background-colors). This example includes the same features as the [Embedded ChatBot demo](/
|
|
136
|
+
This demo displays an embedded ChatBot with a [primary background color](/design-foundations/colors#background-colors). This example includes the same features as the [Embedded ChatBot demo](/extensions/chatbot/overview/demo/#embedded-chatbot)—the only differences are that the background color is adjusted via the `isPrimary` prop and some of the sample Messages have changed. You can use the same logic to adjust the background color in any ChatBot layout.
|
|
137
137
|
|
|
138
138
|
```js file="./WhiteEmbeddedChatbot.tsx" isFullscreen
|
|
139
139
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Ref, FunctionComponent } from 'react';
|
|
2
|
-
import { forwardRef } from 'react';
|
|
2
|
+
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Button, ButtonProps, Icon, Tooltip, TooltipProps } from '@patternfly/react-core';
|
|
5
5
|
import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
|
|
@@ -30,21 +30,43 @@ const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
|
|
|
30
30
|
tooltipContent = 'Chat history menu',
|
|
31
31
|
isCompact,
|
|
32
32
|
...props
|
|
33
|
-
}: ChatbotHeaderMenuProps) =>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
}: ChatbotHeaderMenuProps) => {
|
|
34
|
+
const [isDrawerAnimating, setIsDrawerAnimating] = useState(false);
|
|
35
|
+
// I'd like to use a prop here later if this works
|
|
36
|
+
const drawerState = props['aria-expanded'];
|
|
37
|
+
const isDrawerOpen = drawerState === true;
|
|
38
|
+
const prevDrawerStateRef = useRef<boolean | undefined>(isDrawerOpen);
|
|
39
|
+
const buttonRef = useRef<HTMLButtonElement | null>(null);
|
|
40
|
+
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (drawerState !== undefined) {
|
|
43
|
+
const wasDrawerOpen = prevDrawerStateRef.current === true;
|
|
44
|
+
const isDrawerClosing = wasDrawerOpen && !isDrawerOpen;
|
|
45
|
+
|
|
46
|
+
setIsDrawerAnimating(true);
|
|
47
|
+
const timeout = setTimeout(() => {
|
|
48
|
+
setIsDrawerAnimating(false);
|
|
49
|
+
|
|
50
|
+
if (isDrawerClosing) {
|
|
51
|
+
requestAnimationFrame(() => {
|
|
52
|
+
buttonRef.current?.focus();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}, 350);
|
|
56
|
+
|
|
57
|
+
prevDrawerStateRef.current = isDrawerOpen;
|
|
58
|
+
return () => clearTimeout(timeout);
|
|
59
|
+
}
|
|
60
|
+
}, [drawerState, isDrawerOpen]);
|
|
61
|
+
|
|
62
|
+
const button = useMemo(
|
|
63
|
+
() => (
|
|
42
64
|
<Button
|
|
43
65
|
className={`pf-chatbot__button--toggle-menu ${isCompact ? 'pf-m-compact' : ''}`}
|
|
44
66
|
variant="plain"
|
|
45
67
|
onClick={onMenuToggle}
|
|
46
68
|
aria-label={menuAriaLabel}
|
|
47
|
-
ref={innerRef}
|
|
69
|
+
ref={innerRef ?? buttonRef}
|
|
48
70
|
icon={
|
|
49
71
|
<Icon size={isCompact ? 'lg' : 'xl'} isInline>
|
|
50
72
|
<BarsIcon />
|
|
@@ -53,9 +75,29 @@ const ChatbotHeaderMenuBase: FunctionComponent<ChatbotHeaderMenuProps> = ({
|
|
|
53
75
|
size={isCompact ? 'sm' : undefined}
|
|
54
76
|
{...props}
|
|
55
77
|
/>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
),
|
|
79
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
80
|
+
[isCompact, menuAriaLabel, onMenuToggle, innerRef, buttonRef]
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className={`pf-chatbot__menu ${className}`}>
|
|
85
|
+
{isDrawerAnimating ? (
|
|
86
|
+
button
|
|
87
|
+
) : (
|
|
88
|
+
<Tooltip
|
|
89
|
+
content={tooltipContent}
|
|
90
|
+
position="bottom"
|
|
91
|
+
// prevents VO announcements of both aria label and tooltip
|
|
92
|
+
aria="none"
|
|
93
|
+
{...tooltipProps}
|
|
94
|
+
>
|
|
95
|
+
{button}
|
|
96
|
+
</Tooltip>
|
|
97
|
+
)}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
59
101
|
|
|
60
102
|
export const ChatbotHeaderMenu = forwardRef((props: ChatbotHeaderMenuProps, ref: Ref<HTMLButtonElement>) => (
|
|
61
103
|
<ChatbotHeaderMenuBase innerRef={ref} {...props} />
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
.pf-v6-c-modal-box__footer {
|
|
19
19
|
padding-block-start: var(--pf-t--global--spacer--xl);
|
|
20
20
|
padding-block-end: var(--pf-t--global--spacer--xl);
|
|
21
|
+
border-top: var(--pf-t--global--border--width--high-contrast--regular) solid
|
|
22
|
+
var(--pf-t--global--border--color--high-contrast);
|
|
21
23
|
}
|
|
24
|
+
|
|
22
25
|
.pf-v6-c-modal-box__header {
|
|
23
26
|
padding-block-end: var(--pf-t--global--spacer--sm);
|
|
24
27
|
}
|
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
import type { FunctionComponent, MouseEvent } from 'react';
|
|
6
6
|
import { useState, useEffect, useRef } from 'react';
|
|
7
7
|
import path from 'path-browserify';
|
|
8
|
-
import * as monaco from 'monaco-editor';
|
|
9
|
-
import { loader } from '@monaco-editor/react';
|
|
10
8
|
|
|
11
9
|
// Import PatternFly components
|
|
12
10
|
import { CodeEditor } from '@patternfly/react-code-editor';
|
|
13
11
|
import {
|
|
12
|
+
Bullseye,
|
|
14
13
|
Button,
|
|
15
14
|
getResizeObserver,
|
|
16
15
|
ModalBody,
|
|
17
16
|
ModalFooter,
|
|
18
17
|
ModalHeader,
|
|
18
|
+
Spinner,
|
|
19
19
|
Stack,
|
|
20
20
|
StackItem
|
|
21
21
|
} from '@patternfly/react-core';
|
|
@@ -23,8 +23,16 @@ import FileDetails, { extensionToLanguage } from '../FileDetails';
|
|
|
23
23
|
import { ChatbotDisplayMode } from '../Chatbot';
|
|
24
24
|
import ChatbotModal from '../ChatbotModal/ChatbotModal';
|
|
25
25
|
|
|
26
|
-
//
|
|
27
|
-
|
|
26
|
+
// Try to lazy load - some consumers need to be below a certain bundle size, but can't use the CDN and don't have webpack
|
|
27
|
+
let monacoInstance: typeof import('monaco-editor') | null = null;
|
|
28
|
+
const loadMonaco = async () => {
|
|
29
|
+
if (!monacoInstance) {
|
|
30
|
+
const [monaco, { loader }] = await Promise.all([import('monaco-editor'), import('@monaco-editor/react')]);
|
|
31
|
+
monacoInstance = monaco;
|
|
32
|
+
loader.config({ monaco });
|
|
33
|
+
}
|
|
34
|
+
return monacoInstance;
|
|
35
|
+
};
|
|
28
36
|
|
|
29
37
|
export interface CodeModalProps {
|
|
30
38
|
/** Class applied to code editor */
|
|
@@ -63,6 +71,8 @@ export interface CodeModalProps {
|
|
|
63
71
|
modalBodyClassName?: string;
|
|
64
72
|
/** Class applied to modal footer */
|
|
65
73
|
modalFooterClassName?: string;
|
|
74
|
+
/** Aria label applied to spinner when loading Monaco */
|
|
75
|
+
spinnerAriaLabel?: string;
|
|
66
76
|
}
|
|
67
77
|
|
|
68
78
|
export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
@@ -84,13 +94,32 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
84
94
|
modalHeaderClassName,
|
|
85
95
|
modalBodyClassName,
|
|
86
96
|
modalFooterClassName,
|
|
97
|
+
spinnerAriaLabel = 'Loading',
|
|
87
98
|
...props
|
|
88
99
|
}: CodeModalProps) => {
|
|
89
100
|
const [newCode, setNewCode] = useState(code);
|
|
90
|
-
const [editorInstance, setEditorInstance] = useState<
|
|
101
|
+
const [editorInstance, setEditorInstance] = useState<any>(null);
|
|
91
102
|
const [isEditorReady, setIsEditorReady] = useState(false);
|
|
103
|
+
const [isMonacoLoading, setIsMonacoLoading] = useState(false);
|
|
104
|
+
const [isMonacoLoaded, setIsMonacoLoaded] = useState(false);
|
|
92
105
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
93
106
|
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (isModalOpen && !isMonacoLoaded && !isMonacoLoading) {
|
|
109
|
+
setIsMonacoLoading(true);
|
|
110
|
+
loadMonaco()
|
|
111
|
+
.then(() => {
|
|
112
|
+
setIsMonacoLoaded(true);
|
|
113
|
+
setIsMonacoLoading(false);
|
|
114
|
+
})
|
|
115
|
+
.catch((error) => {
|
|
116
|
+
// eslint-disable-next-line no-console
|
|
117
|
+
console.error('Failed to load Monaco editor:', error);
|
|
118
|
+
setIsMonacoLoading(false);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}, [isModalOpen, isMonacoLoaded, isMonacoLoading]);
|
|
122
|
+
|
|
94
123
|
useEffect(() => {
|
|
95
124
|
if (!isModalOpen || !isEditorReady || !editorInstance || !containerRef.current) {
|
|
96
125
|
return;
|
|
@@ -148,6 +177,42 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
148
177
|
}
|
|
149
178
|
};
|
|
150
179
|
|
|
180
|
+
const renderMonacoEditor = () => {
|
|
181
|
+
if (isMonacoLoading) {
|
|
182
|
+
return (
|
|
183
|
+
<Bullseye>
|
|
184
|
+
<Spinner aria-label={spinnerAriaLabel} />
|
|
185
|
+
</Bullseye>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (isMonacoLoaded) {
|
|
189
|
+
return (
|
|
190
|
+
<CodeEditor
|
|
191
|
+
isDarkTheme
|
|
192
|
+
isLineNumbersVisible={isLineNumbersVisible}
|
|
193
|
+
isLanguageLabelVisible
|
|
194
|
+
isCopyEnabled={isCopyEnabled}
|
|
195
|
+
isReadOnly={isReadOnly}
|
|
196
|
+
code={newCode}
|
|
197
|
+
language={extensionToLanguage[path.extname(fileName).slice(1)]}
|
|
198
|
+
onEditorDidMount={onEditorDidMount}
|
|
199
|
+
onCodeChange={onCodeChange}
|
|
200
|
+
className={codeEditorClassName}
|
|
201
|
+
isFullHeight
|
|
202
|
+
options={{
|
|
203
|
+
glyphMargin: false,
|
|
204
|
+
folding: false,
|
|
205
|
+
// prevents Monaco from handling resizing itself
|
|
206
|
+
// was causing ResizeObserver issues
|
|
207
|
+
automaticLayout: false
|
|
208
|
+
}}
|
|
209
|
+
{...props}
|
|
210
|
+
/>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return null;
|
|
214
|
+
};
|
|
215
|
+
|
|
151
216
|
const modal = (
|
|
152
217
|
<ChatbotModal
|
|
153
218
|
isOpen={isModalOpen}
|
|
@@ -166,27 +231,7 @@ export const CodeModal: FunctionComponent<CodeModalProps> = ({
|
|
|
166
231
|
<FileDetails fileName={fileName} />
|
|
167
232
|
</StackItem>
|
|
168
233
|
<div className="pf-v6-l-stack__item pf-chatbot__code-modal-editor" ref={containerRef}>
|
|
169
|
-
|
|
170
|
-
isDarkTheme
|
|
171
|
-
isLineNumbersVisible={isLineNumbersVisible}
|
|
172
|
-
isLanguageLabelVisible
|
|
173
|
-
isCopyEnabled={isCopyEnabled}
|
|
174
|
-
isReadOnly={isReadOnly}
|
|
175
|
-
code={newCode}
|
|
176
|
-
language={extensionToLanguage[path.extname(fileName).slice(1)]}
|
|
177
|
-
onEditorDidMount={onEditorDidMount}
|
|
178
|
-
onCodeChange={onCodeChange}
|
|
179
|
-
className={codeEditorClassName}
|
|
180
|
-
isFullHeight
|
|
181
|
-
options={{
|
|
182
|
-
glyphMargin: false,
|
|
183
|
-
folding: false,
|
|
184
|
-
// prevents Monaco from handling resizing itself
|
|
185
|
-
// was causing ResizeObserver issues
|
|
186
|
-
automaticLayout: false
|
|
187
|
-
}}
|
|
188
|
-
{...props}
|
|
189
|
-
/>
|
|
234
|
+
{renderMonacoEditor()}
|
|
190
235
|
</div>
|
|
191
236
|
</Stack>
|
|
192
237
|
</ModalBody>
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
.pf-chatbot__onboarding-modal {
|
|
2
|
+
overflow-x: hidden;
|
|
3
|
+
|
|
4
|
+
.pf-chatbot__onboarding--title {
|
|
5
|
+
margin-block-end: var(--pf-t--global--spacer--md);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.pf-chatbot__onboarding--section {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
width: 100%;
|
|
12
|
+
height: 100%;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.pf-chatbot__onboarding--modal-body {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.pf-chatbot__onboarding--modal-text {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
justify-content: flex-end;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.pf-v6-c-content {
|
|
27
|
+
font-size: var(--pf-t--global--font--size--body--lg);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.pf-chatbot__onboarding--header {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
max-height: 65%;
|
|
36
|
+
|
|
37
|
+
img {
|
|
38
|
+
max-width: unset;
|
|
39
|
+
height: 100%;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.pf-chatbot__onboarding--title {
|
|
44
|
+
font-size: var(--pf-t--global--font--size--heading--h1);
|
|
45
|
+
font-family: var(--pf-t--global--font--family--heading);
|
|
46
|
+
font-weight: var(--pf-t--global--font--weight--heading--bold);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.pf-chatbot__onboarding--footer {
|
|
50
|
+
margin-block-start: var(--pf-t--global--spacer--md);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// for handling zoom conditions; zoom to 125% or higher to see this
|
|
54
|
+
@media screen and (max-height: 620px) {
|
|
55
|
+
.pf-v6-c-modal-box__body {
|
|
56
|
+
--pf-v6-c-modal-box__body--MinHeight: auto;
|
|
57
|
+
overflow: visible;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--docked.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--docked,
|
|
63
|
+
.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--fullscreen.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--fullscreen,
|
|
64
|
+
.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--embedded.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--embedded {
|
|
65
|
+
.pf-chatbot__onboarding--header {
|
|
66
|
+
img {
|
|
67
|
+
max-width: 100%;
|
|
68
|
+
height: auto;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--fullscreen.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--fullscreen,
|
|
74
|
+
.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--embedded.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--embedded {
|
|
75
|
+
// override parent modal style
|
|
76
|
+
height: inherit !important;
|
|
77
|
+
|
|
78
|
+
.pf-chatbot__onboarding--title {
|
|
79
|
+
font-size: var(--pf-t--global--font--size--heading--2xl);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.pf-chatbot__onboarding-modal.pf-m-compact {
|
|
84
|
+
.pf-chatbot__onboarding--header {
|
|
85
|
+
gap: var(--pf-t--global--spacer--md);
|
|
86
|
+
align-items: flex-start;
|
|
87
|
+
margin-block-start: var(--pf-t--global--spacer--lg);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.pf-chatbot__onboarding--modal-header {
|
|
91
|
+
--pf-v6-c-modal-box__header--PaddingBlockStart: var(--pf-t--global--spacer--md);
|
|
92
|
+
--pf-v6-c-modal-box__header--PaddingBlockEnd: var(--pf-t--global--spacer--md);
|
|
93
|
+
--pf-v6-c-modal-box__header--PaddingInlineStart: var(--pf-t--global--spacer--md);
|
|
94
|
+
--pf-v6-c-modal-box__header--PaddingInlineEnd: var(--pf-t--global--spacer--md);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.pf-chatbot__onboarding--modal-body {
|
|
98
|
+
--pf-v6-c-modal-box__body--PaddingInlineStart: var(--pf-t--global--spacer--md);
|
|
99
|
+
--pf-v6-c-modal-box__body--PaddingInlineEnd: var(--pf-t--global--spacer--md);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import '@testing-library/jest-dom';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import Onboarding from './Onboarding';
|
|
5
|
+
|
|
6
|
+
const handleModalToggle = jest.fn();
|
|
7
|
+
const onPrimaryAction = jest.fn();
|
|
8
|
+
const onSecondaryAction = jest.fn();
|
|
9
|
+
|
|
10
|
+
const body =
|
|
11
|
+
'Experience personalized assistance and seamless problem-solving, simplifying your journey with Red Hat every step of the way.';
|
|
12
|
+
|
|
13
|
+
describe('Onboarding', () => {
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
it('should render modal correctly', () => {
|
|
18
|
+
render(
|
|
19
|
+
<Onboarding
|
|
20
|
+
isModalOpen
|
|
21
|
+
onSecondaryAction={onSecondaryAction}
|
|
22
|
+
handleModalToggle={handleModalToggle}
|
|
23
|
+
ouiaId="Terms"
|
|
24
|
+
>
|
|
25
|
+
{body}
|
|
26
|
+
</Onboarding>
|
|
27
|
+
);
|
|
28
|
+
expect(screen.getByRole('heading', { name: /Onboarding/i })).toBeTruthy();
|
|
29
|
+
expect(screen.getByRole('button', { name: /Continue/i })).toBeTruthy();
|
|
30
|
+
expect(screen.getByRole('button', { name: /Skip/i })).toBeTruthy();
|
|
31
|
+
expect(screen.getByText(body)).toBeTruthy();
|
|
32
|
+
expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal');
|
|
33
|
+
expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal--default');
|
|
34
|
+
});
|
|
35
|
+
it('should handle image and altText props', () => {
|
|
36
|
+
render(
|
|
37
|
+
<Onboarding
|
|
38
|
+
isModalOpen
|
|
39
|
+
onSecondaryAction={onSecondaryAction}
|
|
40
|
+
handleModalToggle={handleModalToggle}
|
|
41
|
+
headerImage="./image.png"
|
|
42
|
+
headerImageAltText="Test image"
|
|
43
|
+
>
|
|
44
|
+
{body}
|
|
45
|
+
</Onboarding>
|
|
46
|
+
);
|
|
47
|
+
expect(screen.getByRole('img')).toBeTruthy();
|
|
48
|
+
expect(screen.getByRole('img')).toHaveAttribute('alt', 'Test image');
|
|
49
|
+
});
|
|
50
|
+
it('should handle className prop', () => {
|
|
51
|
+
render(
|
|
52
|
+
<Onboarding
|
|
53
|
+
isModalOpen
|
|
54
|
+
onSecondaryAction={onSecondaryAction}
|
|
55
|
+
handleModalToggle={handleModalToggle}
|
|
56
|
+
className="test"
|
|
57
|
+
>
|
|
58
|
+
{body}
|
|
59
|
+
</Onboarding>
|
|
60
|
+
);
|
|
61
|
+
expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal');
|
|
62
|
+
expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal--default');
|
|
63
|
+
expect(screen.getByRole('dialog')).toHaveClass('test');
|
|
64
|
+
});
|
|
65
|
+
it('should handle title prop', () => {
|
|
66
|
+
render(
|
|
67
|
+
<Onboarding
|
|
68
|
+
isModalOpen
|
|
69
|
+
onSecondaryAction={onSecondaryAction}
|
|
70
|
+
handleModalToggle={handleModalToggle}
|
|
71
|
+
title="Updated title"
|
|
72
|
+
>
|
|
73
|
+
{body}
|
|
74
|
+
</Onboarding>
|
|
75
|
+
);
|
|
76
|
+
expect(screen.getByRole('heading', { name: /Updated title/i })).toBeTruthy();
|
|
77
|
+
expect(screen.queryByRole('heading', { name: /Onboarding/i })).toBeFalsy();
|
|
78
|
+
});
|
|
79
|
+
it('should handle primary button prop', () => {
|
|
80
|
+
render(
|
|
81
|
+
<Onboarding
|
|
82
|
+
isModalOpen
|
|
83
|
+
onSecondaryAction={onSecondaryAction}
|
|
84
|
+
handleModalToggle={handleModalToggle}
|
|
85
|
+
primaryActionBtn="First"
|
|
86
|
+
>
|
|
87
|
+
{body}
|
|
88
|
+
</Onboarding>
|
|
89
|
+
);
|
|
90
|
+
expect(screen.getByRole('button', { name: /First/i })).toBeTruthy();
|
|
91
|
+
expect(screen.queryByRole('button', { name: /Continue/i })).toBeFalsy();
|
|
92
|
+
});
|
|
93
|
+
it('should handle secondary button prop', () => {
|
|
94
|
+
render(
|
|
95
|
+
<Onboarding
|
|
96
|
+
isModalOpen
|
|
97
|
+
onSecondaryAction={onSecondaryAction}
|
|
98
|
+
handleModalToggle={handleModalToggle}
|
|
99
|
+
secondaryActionBtn="Second"
|
|
100
|
+
>
|
|
101
|
+
{body}
|
|
102
|
+
</Onboarding>
|
|
103
|
+
);
|
|
104
|
+
expect(screen.getByRole('button', { name: /Second/i })).toBeTruthy();
|
|
105
|
+
expect(screen.queryByRole('button', { name: /Skip/i })).toBeFalsy();
|
|
106
|
+
});
|
|
107
|
+
it('should handle primary button click', async () => {
|
|
108
|
+
render(
|
|
109
|
+
<Onboarding
|
|
110
|
+
isModalOpen
|
|
111
|
+
onPrimaryAction={onPrimaryAction}
|
|
112
|
+
onSecondaryAction={onSecondaryAction}
|
|
113
|
+
handleModalToggle={handleModalToggle}
|
|
114
|
+
>
|
|
115
|
+
{body}
|
|
116
|
+
</Onboarding>
|
|
117
|
+
);
|
|
118
|
+
await userEvent.click(screen.getByRole('button', { name: /Continue/i }));
|
|
119
|
+
expect(onPrimaryAction).toHaveBeenCalledTimes(1);
|
|
120
|
+
expect(handleModalToggle).toHaveBeenCalledTimes(1);
|
|
121
|
+
});
|
|
122
|
+
it('should handle secondary button click', async () => {
|
|
123
|
+
render(
|
|
124
|
+
<Onboarding isModalOpen onSecondaryAction={onSecondaryAction} handleModalToggle={handleModalToggle}>
|
|
125
|
+
{body}
|
|
126
|
+
</Onboarding>
|
|
127
|
+
);
|
|
128
|
+
await userEvent.click(screen.getByRole('button', { name: /Skip/i }));
|
|
129
|
+
expect(onSecondaryAction).toHaveBeenCalledTimes(1);
|
|
130
|
+
expect(handleModalToggle).not.toHaveBeenCalled();
|
|
131
|
+
});
|
|
132
|
+
it('should handle isCompact prop', () => {
|
|
133
|
+
render(
|
|
134
|
+
<Onboarding
|
|
135
|
+
isModalOpen
|
|
136
|
+
onSecondaryAction={onSecondaryAction}
|
|
137
|
+
handleModalToggle={handleModalToggle}
|
|
138
|
+
isCompact={true}
|
|
139
|
+
headerImage="./image.png"
|
|
140
|
+
headerImageAltText="Test image"
|
|
141
|
+
>
|
|
142
|
+
{body}
|
|
143
|
+
</Onboarding>
|
|
144
|
+
);
|
|
145
|
+
expect(screen.getByRole('dialog')).toHaveClass('pf-m-compact');
|
|
146
|
+
expect(screen.queryByRole('img')).toBeFalsy();
|
|
147
|
+
});
|
|
148
|
+
});
|