Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import {
useRef,
useState,
FunctionComponent,
MouseEvent,
CSSProperties,
Ref,
MouseEvent as ReactMouseEvent
} from 'react';
import {
Button,
SkipToContent,
MenuToggle,
MenuToggleElement,
Select,
SelectList,
SelectOption,
Stack
} from '@patternfly/react-core';
import Onboarding from '@patternfly/chatbot/dist/dynamic/Onboarding';
import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';

export const OnboardingExample: FunctionComponent = () => {
const [isModalOpen, setIsModalOpen] = useState(true);
const [displayMode, setDisplayMode] = useState(ChatbotDisplayMode.default);
const chatbotRef = useRef<HTMLDivElement>(null);
const termsRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(false);
const [selected, setSelected] = useState<string>('Select display mode');

const handleSkipToContent = (e) => {
e.preventDefault();
if (!isModalOpen && chatbotRef.current) {
chatbotRef.current.focus();
}
if (isModalOpen && termsRef.current) {
termsRef.current.focus();
}
};

const handleModalToggle = (_event: MouseEvent | MouseEvent | KeyboardEvent) => {
setIsModalOpen(!isModalOpen);
};

const onPrimaryAction = () => {
// eslint-disable-next-line no-console
console.log('Clicked primary action');
};

const onSecondaryAction = () => {
// eslint-disable-next-line no-console
console.log('Clicked secondary action');
};
const onSelect = (_event: ReactMouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => {
setSelected(value as string);
setIsOpen(false);
if (value === 'Default') {
setDisplayMode(ChatbotDisplayMode.default);
}
if (value === 'Docked') {
setDisplayMode(ChatbotDisplayMode.docked);
}
if (value === 'Fullscreen') {
setDisplayMode(ChatbotDisplayMode.fullscreen);
}
if (value === 'Embedded') {
setDisplayMode(ChatbotDisplayMode.embedded);
}
};

const onToggleClick = () => {
setIsOpen(!isOpen);
};

const toggle = (toggleRef: Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
onClick={onToggleClick}
isExpanded={isOpen}
style={
{
width: '200px'
} as CSSProperties
}
aria-label={selected === 'Select display mode' ? 'Select display mode' : `Display mode, ${selected}`}
>
{selected}
</MenuToggle>
);

const body = 'Simplify your Red Hat journey with personalized assistance and seamless problem-solving.';

return (
<>
<SkipToContent style={{ zIndex: '999' }} onClick={handleSkipToContent} href="#">
Skip to chatbot
</SkipToContent>
<div
style={{
position: 'fixed',
padding: 'var(--pf-t--global--spacer--lg)',
zIndex: '601',
boxShadow: 'var(--pf-t--global--box-shadow--lg)'
}}
>
<Stack hasGutter>
<Select
id="single-select"
isOpen={isOpen}
selected={selected}
onSelect={onSelect}
onOpenChange={(isOpen) => setIsOpen(isOpen)}
toggle={toggle}
shouldFocusToggleOnSelect
>
<SelectList>
<SelectOption value="Default">Default</SelectOption>
<SelectOption value="Docked">Docked</SelectOption>
<SelectOption value="Fullscreen">Fullscreen</SelectOption>
<SelectOption value="Embedded">Embedded</SelectOption>
</SelectList>
</Select>
<Button onClick={handleModalToggle}>Toggle modal</Button>
</Stack>
</div>
<Chatbot ref={chatbotRef} displayMode={displayMode} isVisible isCompact></Chatbot>
<Onboarding
ref={termsRef}
displayMode={displayMode}
isModalOpen={isModalOpen}
handleModalToggle={handleModalToggle}
onPrimaryAction={onPrimaryAction}
onSecondaryAction={onSecondaryAction}
title="Redefine work in the age of AI"
isCompact
>
{body}
</Onboarding>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import {
useRef,
useState,
FunctionComponent,
MouseEvent,
CSSProperties,
Ref,
MouseEvent as ReactMouseEvent
} from 'react';
import {
Button,
Checkbox,
SkipToContent,
MenuToggle,
MenuToggleElement,
Select,
SelectList,
SelectOption,
Stack
} from '@patternfly/react-core';
import Onboarding from '@patternfly/chatbot/dist/dynamic/Onboarding';
import Chatbot, { ChatbotDisplayMode } from '@patternfly/chatbot/dist/dynamic/Chatbot';
import onboardingHeader from './RH-Hat-Image.svg';

export const OnboardingExample: FunctionComponent = () => {
const [isModalOpen, setIsModalOpen] = useState(true);
const [displayMode, setDisplayMode] = useState(ChatbotDisplayMode.default);
const [hasImage, setHasImage] = useState(true);
const chatbotRef = useRef<HTMLDivElement>(null);
const termsRef = useRef<HTMLDivElement>(null);
const [isOpen, setIsOpen] = useState(false);
const [selected, setSelected] = useState<string>('Select display mode');

const handleSkipToContent = (e) => {
e.preventDefault();
if (!isModalOpen && chatbotRef.current) {
chatbotRef.current.focus();
}
if (isModalOpen && termsRef.current) {
termsRef.current.focus();
}
};

const handleModalToggle = (_event: MouseEvent | MouseEvent | KeyboardEvent) => {
setIsModalOpen(!isModalOpen);
};

const onPrimaryAction = () => {
// eslint-disable-next-line no-console
console.log('Clicked primary action');
};

const onSecondaryAction = () => {
// eslint-disable-next-line no-console
console.log('Clicked secondary action');
};
const onSelect = (_event: ReactMouseEvent<Element, MouseEvent> | undefined, value: string | number | undefined) => {
setSelected(value as string);
setIsOpen(false);
if (value === 'Default') {
setDisplayMode(ChatbotDisplayMode.default);
}
if (value === 'Docked') {
setDisplayMode(ChatbotDisplayMode.docked);
}
if (value === 'Fullscreen') {
setDisplayMode(ChatbotDisplayMode.fullscreen);
}
if (value === 'Embedded') {
setDisplayMode(ChatbotDisplayMode.embedded);
}
};

const onToggleClick = () => {
setIsOpen(!isOpen);
};

const toggle = (toggleRef: Ref<MenuToggleElement>) => (
<MenuToggle
ref={toggleRef}
onClick={onToggleClick}
isExpanded={isOpen}
style={
{
width: '200px'
} as CSSProperties
}
aria-label={selected === 'Select display mode' ? 'Select display mode' : `Display mode, ${selected}`}
>
{selected}
</MenuToggle>
);

const body = 'Simplify your Red Hat journey with personalized assistance and seamless problem-solving.';

return (
<>
<SkipToContent style={{ zIndex: '999' }} onClick={handleSkipToContent} href="#">
Skip to chatbot
</SkipToContent>
<div
style={{
position: 'fixed',
padding: 'var(--pf-t--global--spacer--lg)',
zIndex: '601',
boxShadow: 'var(--pf-t--global--box-shadow--lg)'
}}
>
<Stack hasGutter>
<Select
id="single-select"
isOpen={isOpen}
selected={selected}
onSelect={onSelect}
onOpenChange={(isOpen) => setIsOpen(isOpen)}
toggle={toggle}
shouldFocusToggleOnSelect
>
<SelectList>
<SelectOption value="Default">Default</SelectOption>
<SelectOption value="Docked">Docked</SelectOption>
<SelectOption value="Fullscreen">Fullscreen</SelectOption>
<SelectOption value="Embedded">Embedded</SelectOption>
</SelectList>
</Select>
<Checkbox
isChecked={hasImage}
id="toggle-header-image"
name="toggle-header-image"
label="Has image in header"
onChange={(_event, checked) => setHasImage(checked)}
></Checkbox>
<Button onClick={handleModalToggle}>Toggle modal</Button>
</Stack>
</div>
<Chatbot ref={chatbotRef} displayMode={displayMode} isVisible></Chatbot>
<Onboarding
ref={termsRef}
displayMode={displayMode}
isModalOpen={isModalOpen}
handleModalToggle={handleModalToggle}
onPrimaryAction={onPrimaryAction}
onSecondaryAction={onSecondaryAction}
headerImage={hasImage ? onboardingHeader : undefined}
title="Redefine work in the age of AI"
>
{body}
</Onboarding>
</>
);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import FileDropZone from '@patternfly/chatbot/dist/dynamic/FileDropZone';
import { PreviewAttachment } from '@patternfly/chatbot/dist/dynamic/PreviewAttachment';
import ChatbotAlert from '@patternfly/chatbot/dist/dynamic/ChatbotAlert';
import TermsOfUse from '@patternfly/chatbot/dist/dynamic/TermsOfUse';
import Onboarding from '@patternfly/chatbot/dist/dynamic/Onboarding';
import {
ChatbotHeader,
ChatbotHeaderCloseButton,
Expand Down Expand Up @@ -85,6 +86,7 @@ import PFHorizontalLogoReverse from './PF-HorizontalLogo-Reverse.svg';
import userAvatar from '../Messages/user_avatar.svg';
import patternflyAvatar from '../Messages/patternfly_avatar.jpg';
import termsAndConditionsHeader from './PF-TermsAndConditionsHeader.svg';
import onboardingHeader from './RH-Hat-Image.svg';
import { CloseIcon, SearchIcon, OutlinedCommentsIcon } from '@patternfly/react-icons';
import { FunctionComponent, FormEvent, useState, useRef, MouseEvent, isValidElement, cloneElement, Children, ReactNode, Ref, MouseEvent as ReactMouseEvent, CSSProperties, useEffect} from 'react';
import FilePreview from '@patternfly/chatbot/dist/dynamic/FilePreview';
Expand Down Expand Up @@ -424,24 +426,6 @@ The drawer can also be used to display a list of basic menu items.

```

### Terms of use

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.

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.

```js file="./TermsOfUse.tsx" isFullscreen

```

### Compact terms of use

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.

```js file="./TermsOfUseCompact.tsx" isFullscreen

```

### Settings

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.
Expand Down Expand Up @@ -469,3 +453,37 @@ Based on the [PatternFly modal](/components/modal), this modal adapts to the Cha
```js file="./ChatbotModal.tsx" isFullscreen

```

### Onboarding

You can use the onboarding modal to introduce users to your ChatBot and provide necessary information. The title, image, and body text are customizable.

```js file="./Onboarding.tsx" isFullscreen

```

### Compact onboarding

To make the onboarding modal compact, with less spacing, pass `isCompact` to the `<Onboarding>` component.

```js file="./CompactOnboarding.tsx" isFullscreen

```

### Terms of use

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.

This example also includes an example of how to use [skip to content](/patternfly-ai/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.

```js file="./TermsOfUse.tsx" isFullscreen

```

### Compact terms of use

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.

```js file="./TermsOfUseCompact.tsx" isFullscreen

```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/module/src/ChatbotModal/ChatbotModal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
.pf-v6-c-modal-box__footer {
padding-block-start: var(--pf-t--global--spacer--xl);
padding-block-end: var(--pf-t--global--spacer--xl);
border-top: var(--pf-t--global--border--width--high-contrast--regular) solid
var(--pf-t--global--border--color--high-contrast);
}

.pf-v6-c-modal-box__header {
padding-block-end: var(--pf-t--global--spacer--sm);
}
Expand Down
Loading
Loading