diff --git a/packages/module/src/Message/Message.test.tsx b/packages/module/src/Message/Message.test.tsx index 81363e5c8..e4bc49833 100644 --- a/packages/module/src/Message/Message.test.tsx +++ b/packages/module/src/Message/Message.test.tsx @@ -962,4 +962,23 @@ describe('Message', () => { const form = container.querySelector('form'); expect(form).toHaveClass('test'); }); + it('should be able to disable markdown parsing', () => { + render(); + // this is looking for markdown syntax that is ordinarily stripped + expect(screen.getByText(/~~~yaml/i)).toBeTruthy(); + }); + it('should be able to pass props to react-markdown, such as disabling tags', () => { + render( + + ); + expect(screen.getByText('Here is some YAML code:')).toBeTruthy(); + // code block isn't rendering + expect(screen.queryByRole('button', { name: 'Copy code' })).toBeFalsy(); + }); }); diff --git a/packages/module/src/Message/Message.tsx b/packages/module/src/Message/Message.tsx index 006677089..bdc547d68 100644 --- a/packages/module/src/Message/Message.tsx +++ b/packages/module/src/Message/Message.tsx @@ -3,7 +3,7 @@ // ============================================================================ import { forwardRef, ReactNode, useEffect, useState } from 'react'; import type { FunctionComponent, HTMLProps, MouseEvent as ReactMouseEvent, Ref } from 'react'; -import Markdown from 'react-markdown'; +import Markdown, { Options } from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { AlertProps, @@ -185,6 +185,10 @@ export interface MessageProps extends Omit, 'role'> { editFormProps?: FormProps; /** Sets message to compact styling. */ isCompact?: boolean; + /** Disables markdown parsing for message, allowing only text input */ + isMarkdownDisabled?: boolean; + /** Allows passing additional props down to markdown parser react-markdown, such as allowedElements and disallowedElements. See https://github.com/remarkjs/react-markdown?tab=readme-ov-file#options for options */ + reactMarkdownProps?: Options; } export const MessageBase: FunctionComponent = ({ @@ -224,6 +228,8 @@ export const MessageBase: FunctionComponent = ({ inputRef, editFormProps, isCompact, + isMarkdownDisabled, + reactMarkdownProps, ...props }: MessageProps) => { const [messageText, setMessageText] = useState(content); @@ -250,6 +256,60 @@ export const MessageBase: FunctionComponent = ({ const date = new Date(); const dateString = timestamp ?? `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; + const handleMarkdown = () => { + if (isMarkdownDisabled) { + return ( + + {messageText} + + ); + } + return ( + , + 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} + {...reactMarkdownProps} + > + {messageText} + + ); + }; + const renderMessage = () => { if (isLoading) { return ; @@ -277,51 +337,7 @@ export const MessageBase: FunctionComponent = ({ return ( <> {beforeMainContent && <>{beforeMainContent}} - {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} - > - {messageText} - - )} + {error ? : handleMarkdown()} ); };