diff --git a/docs/chat-ui-react.md b/docs/chat-ui-react.md index 32cc808..eb19aca 100644 --- a/docs/chat-ui-react.md +++ b/docs/chat-ui-react.md @@ -12,7 +12,7 @@ | [ChatInput({ placeholder, stream, inputAutoFocus, handleError, sendButtonIcon, customCssClasses, onSend, onRetry, })](./chat-ui-react.chatinput.md) | A component that allows user to input message and send to Chat API. | | [ChatPanel(props)](./chat-ui-react.chatpanel.md) | A component that renders a full panel for chat bot interactions. This includes the message bubbles for the conversation, input box with send button, and header (if provided). | | [ChatPopUp(props)](./chat-ui-react.chatpopup.md) | A component that renders a popup button that displays and hides a panel for chat bot interactions. | -| [MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, })](./chat-ui-react.messagebubble.md) | A component that displays the provided message. | +| [MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, renderMarkdown, })](./chat-ui-react.messagebubble.md) | A component that displays the provided message. | | [useComposedCssClasses(builtInClasses, customClasses)](./chat-ui-react.usecomposedcssclasses.md) | useComposedCssClasses merges a component's built-in tailwind classes with custom tailwind classes. | | [useReportAnalyticsEvent()](./chat-ui-react.usereportanalyticsevent.md) | Returns a function to send requests to Yext Analytics API. | diff --git a/docs/chat-ui-react.messagebubble.md b/docs/chat-ui-react.messagebubble.md index da23d33..b4646d4 100644 --- a/docs/chat-ui-react.messagebubble.md +++ b/docs/chat-ui-react.messagebubble.md @@ -9,14 +9,14 @@ A component that displays the provided message. **Signature:** ```typescript -export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, }: MessageBubbleProps): React.JSX.Element; +export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, renderMarkdown, }: MessageBubbleProps): React.JSX.Element; ``` ## Parameters | Parameter | Type | Description | | --- | --- | --- | -| { message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, } | [MessageBubbleProps](./chat-ui-react.messagebubbleprops.md) | | +| { message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, renderMarkdown, } | [MessageBubbleProps](./chat-ui-react.messagebubbleprops.md) | | **Returns:** diff --git a/docs/chat-ui-react.messagebubbleprops.md b/docs/chat-ui-react.messagebubbleprops.md index b5d93c1..e130a3f 100644 --- a/docs/chat-ui-react.messagebubbleprops.md +++ b/docs/chat-ui-react.messagebubbleprops.md @@ -21,6 +21,7 @@ export interface MessageBubbleProps | [linkTarget?](./chat-ui-react.messagebubbleprops.linktarget.md) | | string | _(Optional)_ Link target open behavior on click. | | [message](./chat-ui-react.messagebubbleprops.message.md) | | Message | The message to display. | | [onLinkClick?](./chat-ui-react.messagebubbleprops.onlinkclick.md) | | (href?: string) => void | _(Optional)_ A callback which is called when user clicks a link. | +| [renderMarkdown?](./chat-ui-react.messagebubbleprops.rendermarkdown.md) | | boolean | _(Optional)_ Whether to render the message text as markdown. Defaults to true. | | [showFeedbackButtons?](./chat-ui-react.messagebubbleprops.showfeedbackbuttons.md) | | boolean | _(Optional)_ Whether to show the feedback buttons on the message bubble. Defaults to true. | | [showTimestamp?](./chat-ui-react.messagebubbleprops.showtimestamp.md) | | boolean | _(Optional)_ Whether to show the timestamp of the message with the message bubble. Defaults to true. | diff --git a/docs/chat-ui-react.messagebubbleprops.rendermarkdown.md b/docs/chat-ui-react.messagebubbleprops.rendermarkdown.md new file mode 100644 index 0000000..95c41eb --- /dev/null +++ b/docs/chat-ui-react.messagebubbleprops.rendermarkdown.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [@yext/chat-ui-react](./chat-ui-react.md) > [MessageBubbleProps](./chat-ui-react.messagebubbleprops.md) > [renderMarkdown](./chat-ui-react.messagebubbleprops.rendermarkdown.md) + +## MessageBubbleProps.renderMarkdown property + +Whether to render the message text as markdown. Defaults to true. + +**Signature:** + +```typescript +renderMarkdown?: boolean; +``` diff --git a/etc/chat-ui-react.api.md b/etc/chat-ui-react.api.md index 99612bc..fe9b106 100644 --- a/etc/chat-ui-react.api.md +++ b/etc/chat-ui-react.api.md @@ -178,7 +178,7 @@ export interface InitialMessagePopUpCssClasses { } // @public -export function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, }: MessageBubbleProps): React_2.JSX.Element; +export function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, renderMarkdown, }: MessageBubbleProps): React_2.JSX.Element; // @public export interface MessageBubbleCssClasses { @@ -219,6 +219,7 @@ export interface MessageBubbleProps { linkTarget?: string; message: Message; onLinkClick?: (href?: string) => void; + renderMarkdown?: boolean; showFeedbackButtons?: boolean; showTimestamp?: boolean; } diff --git a/src/components/MessageBubble.tsx b/src/components/MessageBubble.tsx index 87e6597..638f853 100644 --- a/src/components/MessageBubble.tsx +++ b/src/components/MessageBubble.tsx @@ -79,6 +79,11 @@ export interface MessageBubbleProps { linkTarget?: string; /** A callback which is called when user clicks a link. */ onLinkClick?: (href?: string) => void; + /** + * Whether to render the message text as markdown. + * Defaults to true. + */ + renderMarkdown?: boolean; } /** @@ -96,6 +101,7 @@ export function MessageBubble({ formatTimestamp = defaultFormatTimestamp, linkTarget, onLinkClick, + renderMarkdown = true, }: MessageBubbleProps) { const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses); const bubbleCssClasses = twMerge( @@ -140,13 +146,17 @@ export function MessageBubble({ responseId={message.responseId} /> )} - + {renderMarkdown ? ( + + ) : ( +
{message.text}
+ )} {/* fallback on empty space here to perserve the height for timestamp div */} {showTimestamp && ( diff --git a/tests/components/MessageBubble.test.tsx b/tests/components/MessageBubble.test.tsx index d2f6370..3326f57 100644 --- a/tests/components/MessageBubble.test.tsx +++ b/tests/components/MessageBubble.test.tsx @@ -79,3 +79,53 @@ it("applies link target setting (parent)", async () => { expect(screen.getByText("msg link")).toHaveAttribute("target", "_parent"); }); + +it("renders markdown by default", () => { + const linkMessage: Message = { + text: "Test [msg link](msglink)", + timestamp: "2023-06-01T15:26:55.362Z", + source: MessageSource.USER, + }; + + render( + + + + ); + + expect(screen.getByText("msg link")).toHaveAttribute("href", "msglink"); +}); + +it("renders raw message text when renderMarkdown is false", () => { + const linkMessage: Message = { + text: "Test [msg link](msglink)", + timestamp: "2023-06-01T15:26:55.362Z", + source: MessageSource.USER, + }; + + render( + + + + ); + + expect(screen.getByText(linkMessage.text)).toBeInTheDocument(); + expect(screen.queryByRole("link")).not.toBeInTheDocument(); +}); + +it("escapes html when renderMarkdown is false", () => { + const htmlMessage: Message = { + text: "Test", + timestamp: "2023-06-01T15:26:55.362Z", + source: MessageSource.USER, + }; + + render( + + + + ); + + expect(screen.getByText(htmlMessage.text)).toBeInTheDocument(); + expect(screen.queryByText("Test")).not.toBeInTheDocument(); +});