diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/CompactOnboarding.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/CompactOnboarding.tsx new file mode 100644 index 000000000..bc25a6c6d --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/CompactOnboarding.tsx @@ -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(null); + const termsRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + const [selected, setSelected] = useState('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 | 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) => ( + + {selected} + + ); + + const body = 'Simplify your Red Hat journey with personalized assistance and seamless problem-solving.'; + + return ( + <> + + Skip to chatbot + +
+ + + + +
+ + + {body} + + + ); +}; diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/Onboarding.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/Onboarding.tsx new file mode 100644 index 000000000..beca2281e --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/Onboarding.tsx @@ -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(null); + const termsRef = useRef(null); + const [isOpen, setIsOpen] = useState(false); + const [selected, setSelected] = useState('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 | 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) => ( + + {selected} + + ); + + const body = 'Simplify your Red Hat journey with personalized assistance and seamless problem-solving.'; + + return ( + <> + + Skip to chatbot + +
+ + + setHasImage(checked)} + > + + +
+ + + {body} + + + ); +}; diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/RH-Hat-Image.svg b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/RH-Hat-Image.svg new file mode 100644 index 000000000..84a4ce220 --- /dev/null +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/RH-Hat-Image.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md index a2ffc11a0..6b68fe23d 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md @@ -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, @@ -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'; @@ -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 ``. 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. @@ -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 `` 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 ``. 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 + +``` diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/attach-via-menu-of-options-in-message-bar.png b/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/attach-via-menu-of-options-in-message-bar.png new file mode 100644 index 000000000..b8206e985 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/attach-via-menu-of-options-in-message-bar.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/attach-via-upload-button-in-message-bar.png b/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/attach-via-upload-button-in-message-bar.png new file mode 100644 index 000000000..c2d07a747 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/attach-via-upload-button-in-message-bar.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/message-auto-scrolling.png b/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/message-auto-scrolling.png new file mode 100644 index 000000000..208a0898b Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/messages/demo/message-auto-scrolling.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/basic-chatbot.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/basic-chatbot.png new file mode 100644 index 000000000..01d171b11 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/basic-chatbot.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/chat-transcripts.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/chat-transcripts.png new file mode 100644 index 000000000..01d171b11 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/chat-transcripts.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/compact-chatbot.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/compact-chatbot.png new file mode 100644 index 000000000..904d2b06a Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/compact-chatbot.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/comparing-chatbots.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/comparing-chatbots.png new file mode 100644 index 000000000..526271acd Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/comparing-chatbots.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/display-mode-switcher.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/display-mode-switcher.png new file mode 100644 index 000000000..a021b0e14 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/display-mode-switcher.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/embedded-chatbot.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/embedded-chatbot.png new file mode 100644 index 000000000..064159f71 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/embedded-chatbot.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/inline-drawer-chatbot.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/inline-drawer-chatbot.png new file mode 100644 index 000000000..48ec240a5 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/inline-drawer-chatbot.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/primary-color-background.png b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/primary-color-background.png new file mode 100644 index 000000000..a6a22f3c9 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/overview/demo/primary-color-background.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/basic-toggle.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/basic-toggle.png new file mode 100644 index 000000000..89818be08 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/basic-toggle.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-onboarding.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-onboarding.png new file mode 100644 index 000000000..125083bc9 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-onboarding.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-settings.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-settings.png new file mode 100644 index 000000000..295402286 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-settings.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-terms-of-use.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-terms-of-use.png new file mode 100644 index 000000000..954f4fd26 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/compact-terms-of-use.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/container.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/container.png new file mode 100644 index 000000000..f15dfa6a0 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/container.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/custom-toggle-icon.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/custom-toggle-icon.png new file mode 100644 index 000000000..352bada36 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/custom-toggle-icon.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/custom-toggle-shape.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/custom-toggle-shape.png new file mode 100644 index 000000000..7c8fb688d Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/custom-toggle-shape.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/modal.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/modal.png new file mode 100644 index 000000000..d7e302da6 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/modal.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/onboarding.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/onboarding.png new file mode 100644 index 000000000..b33950e59 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/onboarding.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/settings.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/settings.png new file mode 100644 index 000000000..16afcdb92 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/settings.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/skip-to-content.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/skip-to-content.png new file mode 100644 index 000000000..2e5bf6b20 Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/skip-to-content.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/terms-of-use.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/terms-of-use.png new file mode 100644 index 000000000..8aa2fe21b Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/terms-of-use.png differ diff --git a/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/welcome-message.png b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/welcome-message.png new file mode 100644 index 000000000..73d901b7e Binary files /dev/null and b/packages/module/patternfly-docs/generated/extensions/chatbot/ui/react/welcome-message.png differ diff --git a/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/ui/react/compact-onboarding.png b/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/ui/react/compact-onboarding.png new file mode 100644 index 000000000..125083bc9 Binary files /dev/null and b/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/ui/react/compact-onboarding.png differ diff --git a/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/ui/react/onboarding.png b/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/ui/react/onboarding.png new file mode 100644 index 000000000..b33950e59 Binary files /dev/null and b/packages/module/patternfly-docs/generated/patternfly-ai/chatbot/ui/react/onboarding.png differ diff --git a/packages/module/src/ChatbotModal/ChatbotModal.scss b/packages/module/src/ChatbotModal/ChatbotModal.scss index 25deec61e..4bd4524a8 100644 --- a/packages/module/src/ChatbotModal/ChatbotModal.scss +++ b/packages/module/src/ChatbotModal/ChatbotModal.scss @@ -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); } diff --git a/packages/module/src/Onboarding/Onboarding.scss b/packages/module/src/Onboarding/Onboarding.scss new file mode 100644 index 000000000..dad3beff8 --- /dev/null +++ b/packages/module/src/Onboarding/Onboarding.scss @@ -0,0 +1,101 @@ +.pf-chatbot__onboarding-modal { + overflow-x: hidden; + + .pf-chatbot__onboarding--title { + margin-block-end: var(--pf-t--global--spacer--md); + } + + .pf-chatbot__onboarding--section { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + } + + .pf-chatbot__onboarding--modal-body { + display: flex; + flex-direction: column; + } + + .pf-chatbot__onboarding--modal-text { + display: flex; + flex-direction: column; + justify-content: flex-end; + } + + .pf-v6-c-content { + font-size: var(--pf-t--global--font--size--body--lg); + } + + .pf-chatbot__onboarding--header { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + max-height: 65%; + + img { + max-width: unset; + height: 100%; + } + } + + .pf-chatbot__onboarding--title { + font-size: var(--pf-t--global--font--size--heading--h1); + font-family: var(--pf-t--global--font--family--heading); + font-weight: var(--pf-t--global--font--weight--heading--bold); + } + + .pf-chatbot__onboarding--footer { + margin-block-start: var(--pf-t--global--spacer--md); + } + + // for handling zoom conditions; zoom to 125% or higher to see this + @media screen and (max-height: 620px) { + .pf-v6-c-modal-box__body { + --pf-v6-c-modal-box__body--MinHeight: auto; + overflow: visible; + } + } +} + +.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--docked.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--docked, +.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--fullscreen.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--fullscreen, +.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--embedded.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--embedded { + .pf-chatbot__onboarding--header { + img { + max-width: 100%; + height: auto; + } + } +} + +.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--fullscreen.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--fullscreen, +.pf-chatbot__chatbot-modal.pf-chatbot__chatbot-modal--embedded.pf-chatbot__onboarding-modal.pf-chatbot__onboarding-modal--embedded { + // override parent modal style + height: inherit !important; + + .pf-chatbot__onboarding--title { + font-size: var(--pf-t--global--font--size--heading--2xl); + } +} + +.pf-chatbot__onboarding-modal.pf-m-compact { + .pf-chatbot__onboarding--header { + gap: var(--pf-t--global--spacer--md); + align-items: flex-start; + margin-block-start: var(--pf-t--global--spacer--lg); + } + + .pf-chatbot__onboarding--modal-header { + --pf-v6-c-modal-box__header--PaddingBlockStart: var(--pf-t--global--spacer--md); + --pf-v6-c-modal-box__header--PaddingBlockEnd: var(--pf-t--global--spacer--md); + --pf-v6-c-modal-box__header--PaddingInlineStart: var(--pf-t--global--spacer--md); + --pf-v6-c-modal-box__header--PaddingInlineEnd: var(--pf-t--global--spacer--md); + } + + .pf-chatbot__onboarding--modal-body { + --pf-v6-c-modal-box__body--PaddingInlineStart: var(--pf-t--global--spacer--md); + --pf-v6-c-modal-box__body--PaddingInlineEnd: var(--pf-t--global--spacer--md); + } +} diff --git a/packages/module/src/Onboarding/Onboarding.test.tsx b/packages/module/src/Onboarding/Onboarding.test.tsx new file mode 100644 index 000000000..064f1609a --- /dev/null +++ b/packages/module/src/Onboarding/Onboarding.test.tsx @@ -0,0 +1,148 @@ +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import userEvent from '@testing-library/user-event'; +import Onboarding from './Onboarding'; + +const handleModalToggle = jest.fn(); +const onPrimaryAction = jest.fn(); +const onSecondaryAction = jest.fn(); + +const body = + 'Experience personalized assistance and seamless problem-solving, simplifying your journey with Red Hat every step of the way.'; + +describe('Onboarding', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + it('should render modal correctly', () => { + render( + + {body} + + ); + expect(screen.getByRole('heading', { name: /Onboarding/i })).toBeTruthy(); + expect(screen.getByRole('button', { name: /Continue/i })).toBeTruthy(); + expect(screen.getByRole('button', { name: /Skip/i })).toBeTruthy(); + expect(screen.getByText(body)).toBeTruthy(); + expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal'); + expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal--default'); + }); + it('should handle image and altText props', () => { + render( + + {body} + + ); + expect(screen.getByRole('img')).toBeTruthy(); + expect(screen.getByRole('img')).toHaveAttribute('alt', 'Test image'); + }); + it('should handle className prop', () => { + render( + + {body} + + ); + expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal'); + expect(screen.getByRole('dialog')).toHaveClass('pf-chatbot__onboarding-modal--default'); + expect(screen.getByRole('dialog')).toHaveClass('test'); + }); + it('should handle title prop', () => { + render( + + {body} + + ); + expect(screen.getByRole('heading', { name: /Updated title/i })).toBeTruthy(); + expect(screen.queryByRole('heading', { name: /Onboarding/i })).toBeFalsy(); + }); + it('should handle primary button prop', () => { + render( + + {body} + + ); + expect(screen.getByRole('button', { name: /First/i })).toBeTruthy(); + expect(screen.queryByRole('button', { name: /Continue/i })).toBeFalsy(); + }); + it('should handle secondary button prop', () => { + render( + + {body} + + ); + expect(screen.getByRole('button', { name: /Second/i })).toBeTruthy(); + expect(screen.queryByRole('button', { name: /Skip/i })).toBeFalsy(); + }); + it('should handle primary button click', async () => { + render( + + {body} + + ); + await userEvent.click(screen.getByRole('button', { name: /Continue/i })); + expect(onPrimaryAction).toHaveBeenCalledTimes(1); + expect(handleModalToggle).toHaveBeenCalledTimes(1); + }); + it('should handle secondary button click', async () => { + render( + + {body} + + ); + await userEvent.click(screen.getByRole('button', { name: /Skip/i })); + expect(onSecondaryAction).toHaveBeenCalledTimes(1); + expect(handleModalToggle).not.toHaveBeenCalled(); + }); + it('should handle isCompact prop', () => { + render( + + {body} + + ); + expect(screen.getByRole('dialog')).toHaveClass('pf-m-compact'); + expect(screen.queryByRole('img')).toBeFalsy(); + }); +}); diff --git a/packages/module/src/Onboarding/Onboarding.tsx b/packages/module/src/Onboarding/Onboarding.tsx new file mode 100644 index 000000000..47a5b438f --- /dev/null +++ b/packages/module/src/Onboarding/Onboarding.tsx @@ -0,0 +1,126 @@ +// ============================================================================ +// Terms of Use Modal - Chatbot Modal Extension +// ============================================================================ +import type { FunctionComponent, MouseEvent as ReactMouseEvent, Ref } from 'react'; +import { forwardRef } from 'react'; +import { Button, Content, ModalBody, ModalFooter, ModalProps } from '@patternfly/react-core'; +import { ChatbotDisplayMode } from '../Chatbot'; +import ChatbotModal from '../ChatbotModal/ChatbotModal'; + +export interface OnboardingProps extends ModalProps { + /** Class applied to modal */ + className?: string; + /** Action assigned to primary modal button */ + onPrimaryAction?: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void; + /** Action assigned to secondary modal button */ + onSecondaryAction: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void; + /** Name of primary modal button */ + primaryActionBtn?: string; + /** Name of secondary modal button */ + secondaryActionBtn?: string; + /** Function that handles modal toggle */ + handleModalToggle: (event: React.MouseEvent | MouseEvent | KeyboardEvent) => void; + /** Whether modal is open */ + isModalOpen: boolean; + /** Title of modal */ + title?: string; + /** Display mode for the Chatbot parent; this influences the styles applied */ + displayMode?: ChatbotDisplayMode; + /** Optional image displayed in header */ + headerImage?: string; + /** Alt text for optional image displayed in header */ + headerImageAltText?: string; + /** Ref applied to modal */ + innerRef?: React.Ref; + /** OuiaID applied to modal */ + ouiaId?: string; + /** Sets modal to compact styling. */ + isCompact?: boolean; +} + +export const OnboardingBase: FunctionComponent = ({ + handleModalToggle, + isModalOpen, + onPrimaryAction, + onSecondaryAction, + primaryActionBtn = 'Continue', + secondaryActionBtn = 'Skip', + title = 'Onboarding', + headerImage, + headerImageAltText = '', + displayMode = ChatbotDisplayMode.default, + className, + children, + innerRef, + ouiaId = 'Onboarding', + isCompact, + ...props +}: OnboardingProps) => { + const handlePrimaryAction = (_event: ReactMouseEvent | MouseEvent | KeyboardEvent) => { + handleModalToggle(_event); + onPrimaryAction && onPrimaryAction(_event); + }; + + const handleSecondaryAction = (_event: ReactMouseEvent | MouseEvent | KeyboardEvent) => { + onSecondaryAction(_event); + }; + + const modal = ( + + {/* This is a workaround since the PatternFly modal doesn't have ref forwarding */} +
+ <> + + {!isCompact && headerImage && ( +
+ {headerImageAltText} +
+ )} +
+

{title}

+ {children} +
+
+ + + + + +
+
+ ); + + return modal; +}; + +const Onboarding = forwardRef((props: OnboardingProps, ref: Ref) => ( + +)); + +export default Onboarding; diff --git a/packages/module/src/Onboarding/index.ts b/packages/module/src/Onboarding/index.ts new file mode 100644 index 000000000..6dc9b2cdb --- /dev/null +++ b/packages/module/src/Onboarding/index.ts @@ -0,0 +1,3 @@ +export { default } from './Onboarding'; + +export * from './Onboarding'; diff --git a/packages/module/src/index.ts b/packages/module/src/index.ts index 73f999332..757c148bd 100644 --- a/packages/module/src/index.ts +++ b/packages/module/src/index.ts @@ -75,6 +75,9 @@ export * from './MessageBox'; export { default as MessageDivider } from './MessageDivider'; export * from './MessageDivider'; +export { default as Onboarding } from './Onboarding'; +export * from './Onboarding'; + export { default as PreviewAttachment } from './PreviewAttachment'; export * from './PreviewAttachment'; diff --git a/packages/module/src/main.scss b/packages/module/src/main.scss index d8b223dc2..42701d28d 100644 --- a/packages/module/src/main.scss +++ b/packages/module/src/main.scss @@ -31,6 +31,7 @@ @import './MessageBox/MessageBox'; @import './MessageDivider/MessageDivider'; @import './MessageBox/JumpButton'; +@import './Onboarding/Onboarding'; @import './ResponseActions/ResponseActions'; @import './Settings/Settings'; @import './SourcesCard/SourcesCard.scss';