Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option for a footer and a custom header in the ChatPopUp #41

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion THIRD-PARTY-NOTICES
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ SOFTWARE.
The following npm packages may be included in this product:

- @types/[email protected]
- @types/[email protected].11
- @types/[email protected].12
- @types/[email protected]
- @types/[email protected]
- @types/[email protected]
Expand Down
13 changes: 13 additions & 0 deletions docs/chat-ui-react.chatpanelprops.footer.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; [ChatPanelProps](./chat-ui-react.chatpanelprops.md) &gt; [footer](./chat-ui-react.chatpanelprops.footer.md)

## ChatPanelProps.footer property

A footer component to render at the bottom of the panel

**Signature:**

```typescript
footer?: React.ReactNode;
```
2 changes: 1 addition & 1 deletion docs/chat-ui-react.chatpanelprops.header.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ A header to render at the top of the panel.
**Signature:**

```typescript
header?: JSX.Element;
header?: React.ReactNode;
```
3 changes: 2 additions & 1 deletion docs/chat-ui-react.chatpanelprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ export interface ChatPanelProps extends Omit<MessageBubbleProps, "customCssClass
| Property | Modifiers | Type | Description |
| --- | --- | --- | --- |
| [customCssClasses?](./chat-ui-react.chatpanelprops.customcssclasses.md) | | [ChatPanelCssClasses](./chat-ui-react.chatpanelcssclasses.md) | _(Optional)_ CSS classes for customizing the component styling. |
| [header?](./chat-ui-react.chatpanelprops.header.md) | | JSX.Element | _(Optional)_ A header to render at the top of the panel. |
| [footer?](./chat-ui-react.chatpanelprops.footer.md) | | React.ReactNode | _(Optional)_ A footer component to render at the bottom of the panel |
| [header?](./chat-ui-react.chatpanelprops.header.md) | | React.ReactNode | _(Optional)_ A header to render at the top of the panel. |

4 changes: 2 additions & 2 deletions docs/chat-ui-react.chatpopupprops.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ The props for the [ChatPopUp()](./chat-ui-react.chatpopup.md) component.
**Signature:**

```typescript
export interface ChatPopUpProps extends Omit<ChatHeaderProps, "showCloseButton" | "customCssClasses">, Omit<ChatPanelProps, "header" | "customCssClasses">
export interface ChatPopUpProps extends Omit<ChatHeaderProps, "showCloseButton" | "customCssClasses">, Omit<ChatPanelProps, "customCssClasses">
```
**Extends:** Omit&lt;[ChatHeaderProps](./chat-ui-react.chatheaderprops.md)<!-- -->, "showCloseButton" \| "customCssClasses"&gt;, Omit&lt;[ChatPanelProps](./chat-ui-react.chatpanelprops.md)<!-- -->, "header" \| "customCssClasses"&gt;
**Extends:** Omit&lt;[ChatHeaderProps](./chat-ui-react.chatheaderprops.md)<!-- -->, "showCloseButton" \| "customCssClasses"&gt;, Omit&lt;[ChatPanelProps](./chat-ui-react.chatpanelprops.md)<!-- -->, "customCssClasses"&gt;

## Properties

Expand Down
5 changes: 3 additions & 2 deletions etc/chat-ui-react.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export interface ChatPanelCssClasses {
// @public
export interface ChatPanelProps extends Omit<MessageBubbleProps, "customCssClasses" | "message">, Omit<ChatInputProps, "customCssClasses"> {
customCssClasses?: ChatPanelCssClasses;
header?: JSX.Element;
footer?: React_2.ReactNode;
header?: React_2.ReactNode;
}

// @public
Expand Down Expand Up @@ -113,7 +114,7 @@ export interface ChatPopUpCssClasses {
}

// @public
export interface ChatPopUpProps extends Omit<ChatHeaderProps, "showCloseButton" | "customCssClasses">, Omit<ChatPanelProps, "header" | "customCssClasses"> {
export interface ChatPopUpProps extends Omit<ChatHeaderProps, "showCloseButton" | "customCssClasses">, Omit<ChatPanelProps, "customCssClasses"> {
customCssClasses?: ChatPopUpCssClasses;
openPanelButtonIcon?: JSX.Element;
}
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

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

14 changes: 8 additions & 6 deletions src/components/ChatPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ export interface ChatPanelProps
extends Omit<MessageBubbleProps, "customCssClasses" | "message">,
Omit<ChatInputProps, "customCssClasses"> {
/** A header to render at the top of the panel. */
header?: JSX.Element;
header?: React.ReactNode;
yen-tt marked this conversation as resolved.
Show resolved Hide resolved
/**
* A footer component to render at the bottom of the panel
*/
footer?: React.ReactNode;
/**
* CSS classes for customizing the component styling.
*/
Expand All @@ -65,7 +69,7 @@ export interface ChatPanelProps
* @param props - {@link ChatPanelProps}
*/
export function ChatPanel(props: ChatPanelProps) {
const { header, customCssClasses } = props;
const { header, customCssClasses, footer } = props;
const chat = useChatActions();
const messages = useChatState((state) => state.conversation.messages);
const loading = useChatState((state) => state.conversation.isLoading);
Expand Down Expand Up @@ -125,10 +129,7 @@ export function ChatPanel(props: ChatPanelProps) {
<div className={cssClasses.container}>
{header}
<div className={cssClasses.messagesScrollContainer}>
<div
ref={messagesContainer}
className={cssClasses.messagesContainer}
>
<div ref={messagesContainer} className={cssClasses.messagesContainer}>
{messages.map((message, index) => (
<div key={index} ref={setMessagesRef(index)}>
<MessageBubble
Expand All @@ -144,6 +145,7 @@ export function ChatPanel(props: ChatPanelProps) {
<div className={cssClasses.inputContainer}>
<ChatInput {...props} customCssClasses={cssClasses.inputCssClasses} />
</div>
{footer}
</div>
</div>
);
Expand Down
23 changes: 15 additions & 8 deletions src/components/ChatPopUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const builtInCssClasses: ChatPopUpCssClasses = withStylelessCssClasses(
*/
export interface ChatPopUpProps
extends Omit<ChatHeaderProps, "showCloseButton" | "customCssClasses">,
Omit<ChatPanelProps, "header" | "customCssClasses"> {
Omit<ChatPanelProps, "customCssClasses"> {
/** Custom icon for the popup button to open the panel. */
openPanelButtonIcon?: JSX.Element;
/**
Expand All @@ -88,6 +88,8 @@ export function ChatPopUp(props: ChatPopUpProps) {
showRestartButton = true,
onClose: customOnClose,
title,
footer,
header,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thinking about it more, this wouldn't work because the header component needs to take in the onClose function below in order to control the display behavior of the chat popup/panel. We would need to take in a header component type and construct it inside.

} = props;
const reportAnalyticsEvent = useReportAnalyticsEvent();

Expand Down Expand Up @@ -125,14 +127,19 @@ export function ChatPopUp(props: ChatPopUpProps) {
{...props}
customCssClasses={cssClasses.panelCssClasses}
header={
<ChatHeader
title={title}
showRestartButton={showRestartButton}
showCloseButton={true}
onClose={onClose}
customCssClasses={cssClasses.headerCssClasses}
/>
header ? (
header
) : (
<ChatHeader
title={title}
showRestartButton={showRestartButton}
showCloseButton={true}
onClose={onClose}
customCssClasses={cssClasses.headerCssClasses}
/>
)
}
footer={footer}
/>
</div>
<button
Expand Down
17 changes: 17 additions & 0 deletions tests/components/ChatPanel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ const meta: Meta<typeof ChatPanel> = {
};
export default meta;

export const Footer = () => {
return (
<div className="yext-chat__footer text-center text-slate-400 rounded-b-3xl px-4 pb-4 text-[12px]">
This is a footer example
</div>
);
};

export const Primary: StoryObj<typeof meta> = {
render: (args) => (
<DummyChatHeadlessProvider>
Expand All @@ -28,3 +36,12 @@ export const PanelWithHeader: StoryObj<typeof meta> = {
stream: false,
},
};

export const PanelWithFooter: StoryObj<typeof meta> = {
...Primary,
args: {
header: <ChatHeader title="My Chatbot" showRestartButton={true} />,
footer: <Footer />,
stream: false,
},
};
67 changes: 67 additions & 0 deletions tests/components/ChatPopUp.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,61 @@ const styles = {
height: "80vh",
};

const YextLogo = () => {
return (
<svg
width="48"
height="48"
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="chat-yext-logo"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.16003 23.9779C8.16003 15.242 15.252 8.15991 24 8.15991C32.7481 8.15991 39.84 15.242 39.84 23.9779C39.84 32.7139 32.7481 39.796 24 39.796C15.252 39.796 8.16003 32.7139 8.16003 23.9779ZM9.42723 23.9779C9.42723 32.0153 15.9516 38.5305 24 38.5305C32.0485 38.5305 38.5728 32.0153 38.5728 23.9779C38.5728 15.9406 32.0485 9.42535 24 9.42535C15.9516 9.42535 9.42723 15.9406 9.42723 23.9779Z"
fill="white"
/>
<path
d="M24.4752 25.7178H27.3264V31.4123H28.5936V25.7178H31.4448V24.4524H24.4752V25.7178Z"
fill="white"
/>
<path
d="M22.787 24.2942L20.0401 27.0377L17.2931 24.2942L16.3969 25.1892L19.1442 27.9323L16.3969 30.6754L17.2931 31.5705L20.0401 28.8269L22.787 31.5705L23.6833 30.6754L20.9359 27.9323L23.6833 25.1892L22.787 24.2942Z"
fill="white"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M31.4449 19.9444C31.4449 21.9098 29.849 23.5034 27.8809 23.5034C25.9123 23.5034 24.3169 21.9102 24.3169 19.9448C24.3169 17.9794 25.9123 16.3857 27.8809 16.3857C29.1349 16.3857 30.2367 17.033 30.872 18.0106L29.9445 18.9368L26.872 22.0051C27.1765 22.1541 27.5188 22.238 27.8809 22.238C29.1494 22.238 30.1777 21.2111 30.1777 19.9444H31.4449ZM29.0935 17.997C28.7415 17.7777 28.3262 17.6507 27.8809 17.6507C26.6124 17.6507 25.5841 18.6776 25.5845 19.9444C25.5845 20.389 25.7113 20.8038 25.9308 21.1553L29.0935 17.997Z"
fill="white"
/>
<path
d="M20.0401 19.5656L17.3675 16.3853L16.3969 17.1986L19.4065 20.7805V23.5034H20.6737V20.7805L23.6833 17.1986L22.7126 16.3853L20.0401 19.5656Z"
fill="white"
/>
</svg>
);
};

const Header = () => {
return (
<div className="flex items-center bg-black p-1 rounded-t-3xl">
<YextLogo />
<h1 className="text-white ps-2">This is a custom header</h1>
</div>
);
};

const Footer = () => {
return (
<p className="pb-4 text-center text-slate-400 text-[12px]">
This is a footer
</p>
);
};

export const PopupButton: StoryObj<typeof meta> = {
render: (args) => (
<DummyChatHeadlessProvider>
Expand All @@ -39,3 +94,15 @@ export const PopupPanel: StoryObj<typeof meta> = {
userEvent.click(canvas.getByLabelText("Chat Popup Button"));
},
};

export const PopupPanelWithCustomHeaderAndFooter: StoryObj<typeof meta> = {
...PopupButton,
play: ({ canvasElement }) => {
const canvas = within(canvasElement);
userEvent.click(canvas.getByLabelText("Chat Popup Button"));
},
args: {
header: <Header />,
footer: <Footer />,
},
};