Skip to content

Commit

Permalink
custom error handling for message suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
Yen Truong committed Mar 6, 2024
1 parent 099bcd1 commit 80d11b6
Show file tree
Hide file tree
Showing 15 changed files with 84 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## MessageSuggestionsProps.customCssClasses property

CSS classes for customizing the component styling.

**Signature:**

```typescript
Expand Down
13 changes: 13 additions & 0 deletions docs/chat-ui-react.messagesuggestionsprops.handleerror.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@yext/chat-ui-react](./chat-ui-react.md) &gt; [MessageSuggestionsProps](./chat-ui-react.messagesuggestionsprops.md) &gt; [handleError](./chat-ui-react.messagesuggestionsprops.handleerror.md)

## MessageSuggestionsProps.handleError property

A function which is called when an error occurs from Chat API while processing the user's message. By default, the error is logged to the console and an error message is added to state.

**Signature:**

```typescript
handleError?: (e: unknown) => void;
```
5 changes: 3 additions & 2 deletions docs/chat-ui-react.messagesuggestionsprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface MessageSuggestionsProps

| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [customCssClasses?](./chat-ui-react.messagesuggestionsprops.customcssclasses.md) | | [MessageSuggestionCssClasses](./chat-ui-react.messagesuggestioncssclasses.md) | _(Optional)_ |
| [suggestions](./chat-ui-react.messagesuggestionsprops.suggestions.md) | | string\[\] | |
| [customCssClasses?](./chat-ui-react.messagesuggestionsprops.customcssclasses.md) | | [MessageSuggestionCssClasses](./chat-ui-react.messagesuggestioncssclasses.md) | _(Optional)_ CSS classes for customizing the component styling. |
| [handleError?](./chat-ui-react.messagesuggestionsprops.handleerror.md) | | (e: unknown) =&gt; void | _(Optional)_ A function which is called when an error occurs from Chat API while processing the user's message. By default, the error is logged to the console and an error message is added to state. |
| [suggestions](./chat-ui-react.messagesuggestionsprops.suggestions.md) | | string\[\] | List of clickable message suggestions to render. |

2 changes: 2 additions & 0 deletions docs/chat-ui-react.messagesuggestionsprops.suggestions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## MessageSuggestionsProps.suggestions property

List of clickable message suggestions to render.

**Signature:**

```typescript
Expand Down
3 changes: 1 addition & 2 deletions etc/chat-ui-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,8 @@ export interface MessageSuggestionCssClasses {

// @public
export interface MessageSuggestionsProps {
// (undocumented)
customCssClasses?: MessageSuggestionCssClasses;
// (undocumented)
handleError?: (e: unknown) => void;
suggestions: string[];
}

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yext/chat-ui-react",
"version": "0.8.7",
"version": "0.8.8",
"description": "A library of React Components for powering Yext Chat integrations.",
"author": "[email protected]",
"main": "./lib/commonjs/src/index.js",
Expand Down
8 changes: 5 additions & 3 deletions src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ export function ChatInput({
? chat.streamNextMessage(input)
: chat.getNextMessage(input);
setInput("");
res.then(() => {
onSend?.(input)
}).catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
res
.then(() => {
onSend?.(input);
})
.catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
}, [chat, input, handleError, defaultHandleApiError, stream, onSend]);

const handleKeyDown = useCallback(
Expand Down
1 change: 1 addition & 0 deletions src/components/ChatPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ export function ChatPanel(props: ChatPanelProps) {
<div className={cssClasses.inputContainer}>
{suggestions && (
<MessageSuggestions
handleError={handleError}
suggestions={suggestions}
customCssClasses={cssClasses.messageSuggestionClasses}
/>
Expand Down
10 changes: 8 additions & 2 deletions src/components/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export function Markdown({
responseId,
},
});
onLinkClick?.(href)
onLinkClick?.(href);
};
return {
a: ({ node: _, children, ...props }) => {
Expand All @@ -95,7 +95,13 @@ export function Markdown({
);
},
};
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);
}, [
reportAnalyticsEvent,
linkClickEvent,
responseId,
cssClasses,
onLinkClick,
]);

return (
<ReactMarkdown
Expand Down
9 changes: 7 additions & 2 deletions src/components/MessageSuggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ export interface MessageSuggestionCssClasses {
* @public
*/
export interface MessageSuggestionsProps {
/** List of clickable message suggestions to render. */
suggestions: string[];
/** {@inheritdoc ChatInputProps.handleError} */
handleError?: (e: unknown) => void;
/** CSS classes for customizing the component styling. */
customCssClasses?: MessageSuggestionCssClasses;
}

Expand All @@ -44,6 +48,7 @@ const defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(
* @internal
*/
export const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({
handleError,
suggestions,
customCssClasses,
}) => {
Expand All @@ -58,9 +63,9 @@ export const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({
} satisfies MessageNotes;
actions.setMessageNotes(newNotes);
const res = actions.getNextMessage(msg);
res.catch(defaultHandleApiError);
res.catch(handleError ?? defaultHandleApiError);
},
[actions, notes, defaultHandleApiError]
[actions, notes, handleError, defaultHandleApiError]
);

const classes = useComposedCssClasses(defaultClassnames, customCssClasses);
Expand Down
2 changes: 1 addition & 1 deletion test-site/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tests/components/ChatInput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ it("executes custom handleError if provided", async () => {

it("executes onSend if provided", async () => {
const onSendCb = jest.fn();
render(<ChatInput onSend={message => onSendCb(message)} />);
render(<ChatInput onSend={(message) => onSendCb(message)} />);
await act(() => userEvent.type(screen.getByRole("textbox"), "test"));
const sendButton = screen.getByRole("button");
await act(() => userEvent.click(sendButton));
Expand Down
19 changes: 13 additions & 6 deletions tests/components/ChatPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,23 @@ it("executes onLinkClick if provided", async () => {
const onLinkClickCb = jest.fn();
mockChatState({
conversation: {
messages: [{text: "Test [msg link](msglink)"}],
messages: [{ text: "Test [msg link](msglink)" }],
isLoading: false,
canSendMessage: true,
},
});
render(<ChatPanel
footer="Test [footer link](footerlink)"
onLinkClick={href => onLinkClickCb(href)} />);
await act(() => userEvent.click(screen.getByRole("link", { name: "footer link"})));
render(
<ChatPanel
footer="Test [footer link](footerlink)"
onLinkClick={(href) => onLinkClickCb(href)}
/>
);
await act(() =>
userEvent.click(screen.getByRole("link", { name: "footer link" }))
);
expect(onLinkClickCb).toBeCalledWith("footerlink");
await act(() => userEvent.click(screen.getByRole("link", { name: "msg link" })));
await act(() =>
userEvent.click(screen.getByRole("link", { name: "msg link" }))
);
expect(onLinkClickCb).toBeCalledWith("msglink");
});
25 changes: 24 additions & 1 deletion tests/components/MessageSuggestions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

import { act, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { mockChatHooks, spyOnActions } from "../__utils__/mocks";
import {
mockChatActions,
mockChatHooks,
spyOnActions,
} from "../__utils__/mocks";
import { MessageSuggestions } from "../../src/components/MessageSuggestions";

beforeEach(() => {
Expand Down Expand Up @@ -42,3 +46,22 @@ it("clears note replies when pill is clicked", async () => {
suggestedReplies: undefined,
});
});

it("executes custom handleError if provided", async () => {
mockChatActions({
getNextMessage: jest.fn(() => Promise.reject("API Error")),
report: jest.fn(),
setMessageNotes: jest.fn(),
});
const customHandleError = jest.fn();
render(
<MessageSuggestions
handleError={customHandleError}
suggestions={["test msg"]}
/>
);
const button = screen.getByRole("button");
expect(button).toHaveTextContent("test msg");
await act(() => userEvent.click(button));
expect(customHandleError).toBeCalledWith("API Error");
});

0 comments on commit 80d11b6

Please sign in to comment.