diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx index 8f855e213..e24d28448 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/BotMessage.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Message from '@patternfly/chatbot/dist/dynamic/Message'; import patternflyAvatar from './patternfly_avatar.jpg'; import squareImg from './PF-social-color-square.svg'; -import { Form, FormGroup, Radio } from '@patternfly/react-core'; +import { AlertActionLink, Form, FormGroup, Radio } from '@patternfly/react-core'; export const BotMessageExample: React.FunctionComponent = () => { const [variant, setVariant] = React.useState('code'); @@ -140,6 +140,21 @@ _Italic text, formatted with single underscores_ const image = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`; + const error = { + title: 'Could not load chat', + children: 'Wait a few minutes and check your network settings. If the issue persists: ', + actionLinks: ( + + + Start a new chat + + + Contact support + + + ) + }; + return ( <> + setVariant('error')} + name="bot-message-error" + label="Error" + id="error" + /> ); diff --git a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx index af45b8128..148f29e5f 100644 --- a/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx +++ b/packages/module/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessage.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Message from '@patternfly/chatbot/dist/dynamic/Message'; import userAvatar from './user_avatar.svg'; -import { Form, FormGroup, Radio } from '@patternfly/react-core'; +import { AlertActionLink, Form, FormGroup, Radio } from '@patternfly/react-core'; export const UserMessageExample: React.FunctionComponent = () => { const [variant, setVariant] = React.useState('code'); @@ -140,6 +140,21 @@ _Italic text, formatted with single underscores_ const image = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`; + const error = { + title: 'Could not load chat', + children: 'Wait a few minutes and check your network settings. If the issue persists: ', + actionLinks: ( + + + Start a new chat + + + Contact support + + + ) + }; + return ( <> + setVariant('error')} + name="user-message-error" + label="Error" + id="user-error" + /> ); diff --git a/packages/module/src/Message/ErrorMessage/ErrorMessage.tsx b/packages/module/src/Message/ErrorMessage/ErrorMessage.tsx new file mode 100644 index 000000000..cdfa78a5f --- /dev/null +++ b/packages/module/src/Message/ErrorMessage/ErrorMessage.tsx @@ -0,0 +1,14 @@ +// ============================================================================ +// Chatbot Main - Message - Content - Error +// ============================================================================ + +import React from 'react'; +import { Alert, AlertProps } from '@patternfly/react-core'; + +const ErrorMessage = ({ title, actionLinks, children, ...props }: AlertProps) => ( + + {children} + +); + +export default ErrorMessage; diff --git a/packages/module/src/Message/Message.test.tsx b/packages/module/src/Message/Message.test.tsx index 567049476..d16ed1cf4 100644 --- a/packages/module/src/Message/Message.test.tsx +++ b/packages/module/src/Message/Message.test.tsx @@ -6,6 +6,7 @@ import userEvent from '@testing-library/user-event'; import { monitorSampleAppQuickStart } from './QuickStarts/monitor-sampleapp-quickstart'; import { monitorSampleAppQuickStartWithImage } from './QuickStarts/monitor-sampleapp-quickstart-with-image'; import rehypeExternalLinks from '../__mocks__/rehype-external-links'; +import { AlertActionLink } from '@patternfly/react-core'; const ALL_ACTIONS = [ { label: /Good response/i }, @@ -141,6 +142,20 @@ const EMPTY_TABLE = ` const IMAGE = `![Multi-colored wavy lines on a black background](https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif)`; +const ERROR = { + title: 'Could not load chat', + children: 'Wait a few minutes and check your network settings. If the issue persists: ', + actionLinks: ( + + + Start a new chat + + + Contact support + + + ) +}; const checkListItemsRendered = () => { const items = ['Item 1', 'Item 2', 'Item 3']; expect(screen.getAllByRole('listitem')).toHaveLength(3); @@ -769,4 +784,21 @@ describe('Message', () => { // we are mocking rehype libraries, so we can't test target _blank addition on links directly with RTL expect(rehypeExternalLinks).not.toHaveBeenCalled(); }); + it('should handle error correctly', () => { + render(); + expect(screen.getByRole('heading', { name: /Could not load chat/i })).toBeTruthy(); + expect(screen.getByRole('link', { name: /Start a new chat/i })).toBeTruthy(); + expect(screen.getByRole('link', { name: /Contact support/i })).toBeTruthy(); + expect(screen.getByText('Wait a few minutes and check your network settings. If the issue persists:')).toBeTruthy(); + }); + it('should handle error correctly when loading', () => { + render(); + expect(screen.queryByRole('heading', { name: /Could not load chat/i })).toBeFalsy(); + expect(screen.getByText('Loading message')).toBeTruthy(); + }); + it('should handle error correctly when these is content', () => { + render(); + expect(screen.getByRole('heading', { name: /Could not load chat/i })).toBeTruthy(); + expect(screen.queryByText('Test')).toBeFalsy(); + }); }); diff --git a/packages/module/src/Message/Message.tsx b/packages/module/src/Message/Message.tsx index 06778e71e..ad5809b0a 100644 --- a/packages/module/src/Message/Message.tsx +++ b/packages/module/src/Message/Message.tsx @@ -7,6 +7,7 @@ import React, { ReactNode } from 'react'; import Markdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { + AlertProps, Avatar, AvatarProps, ContentVariants, @@ -42,6 +43,7 @@ import rehypeExternalLinks from 'rehype-external-links'; import rehypeSanitize from 'rehype-sanitize'; import { PluggableList } from 'react-markdown/lib'; import LinkMessage from './LinkMessage/LinkMessage'; +import ErrorMessage from './ErrorMessage/ErrorMessage'; export interface MessageAttachment { /** Name of file attached to the message */ @@ -141,6 +143,8 @@ export interface MessageProps extends Omit, 'rol additionalRehypePlugins?: PluggableList; /** Whether to open links in message in new tab. */ openLinkInNewTab?: boolean; + /** Optional inline error message that can be displayed in the message */ + error?: AlertProps; } export const MessageBase: React.FunctionComponent = ({ @@ -169,6 +173,7 @@ export const MessageBase: React.FunctionComponent = ({ tableProps, openLinkInNewTab = true, additionalRehypePlugins = [], + error, ...props }: MessageProps) => { const { beforeMainContent, afterMainContent, endContent } = extraContent || {}; @@ -225,47 +230,51 @@ export const MessageBase: React.FunctionComponent = ({ ) : ( <> {beforeMainContent && <>{beforeMainContent}} - , - code: ({ children, ...props }) => ( - - {children} - - ), - h1: (props) => , - h2: (props) => , - h3: (props) => , - h4: (props) => , - h5: (props) => , - h6: (props) => , - blockquote: (props) => , - ul: (props) => , - ol: (props) => , - li: (props) => , - table: (props) => , - tbody: (props) => , - thead: (props) => , - tr: (props) => , - td: (props) => { - // Conflicts with Td type - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { width, ...rest } = props; - return ; - }, - th: (props) => , - img: (props) => , - a: (props) => ( - - {props.children} - - ) - }} - remarkPlugins={[remarkGfm]} - rehypePlugins={rehypePlugins} - > - {content} - + {error ? ( + + ) : ( + , + code: ({ children, ...props }) => ( + + {children} + + ), + h1: (props) => , + h2: (props) => , + h3: (props) => , + h4: (props) => , + h5: (props) => , + h6: (props) => , + blockquote: (props) => , + ul: (props) => , + ol: (props) => , + li: (props) => , + table: (props) => , + tbody: (props) => , + thead: (props) => , + tr: (props) => , + td: (props) => { + // Conflicts with Td type + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { width, ...rest } = props; + return ; + }, + th: (props) => , + img: (props) => , + a: (props) => ( + + {props.children} + + ) + }} + remarkPlugins={[remarkGfm]} + rehypePlugins={rehypePlugins} + > + {content} + + )} {afterMainContent && <>{afterMainContent}} )}