diff --git a/.changeset/pretty-melons-punch.md b/.changeset/pretty-melons-punch.md index 3b8d854eb0..fe79eb8599 100644 --- a/.changeset/pretty-melons-punch.md +++ b/.changeset/pretty-melons-punch.md @@ -3,4 +3,4 @@ "@twilio-paste/core": patch --- -[Button] Add border radius 20 to size="reset" buttons which can be overridden by passing a border radius token to variant="reset" and size="reset" buttons \ No newline at end of file +[Button] Add border radius 20 to size="reset" buttons which can be overridden by passing a border radius token to variant="reset" and size="reset" buttons diff --git a/cypress/integration/sitemap-vrt/constants.ts b/cypress/integration/sitemap-vrt/constants.ts index 49e88dd27e..c7367f49f6 100644 --- a/cypress/integration/sitemap-vrt/constants.ts +++ b/cypress/integration/sitemap-vrt/constants.ts @@ -24,6 +24,9 @@ export const SITEMAP = [ "/components/account-switcher/", "/components/account-switcher/api", "/components/account-switcher/changelog", + "/components/ai-chat-log/", + "/components/ai-chat-log/api", + "/components/ai-chat-log/changelog", "/components/aspect-ratio/", "/components/aspect-ratio/api", "/components/aspect-ratio/changelog", diff --git a/packages/paste-core/components/ai-chat-log/CHANGELOG.md b/packages/paste-core/components/ai-chat-log/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/paste-website/src/component-examples/AIChatLogExamples.ts b/packages/paste-website/src/component-examples/AIChatLogExamples.ts new file mode 100644 index 0000000000..4a86abaea5 --- /dev/null +++ b/packages/paste-website/src/component-examples/AIChatLogExamples.ts @@ -0,0 +1,302 @@ +export const basicBotMessage = ` +const BasicMessage = () => { + return ( + + + Good Bot + + Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS. + + + + ); +}; + +render( + +)`.trim(); +export const basicHumanMessage = ` +const BasicMessage = () => { + return ( + + + Gibby Radki + + I would like some information on twilio error codes for undelivered messages + + + + ); +}; + +render( + +)`.trim(); +export const botWithFeedback = ` +const MessageWithFeedback = () => { + return ( + + + Good Bot + + Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS. + + + + Is this helpful? + + + + + + + + + + + ); +}; + +render( + +)`.trim(); +export const botWithBodyActions = ` +const MessageWithFeedback = () => { + return ( + + + Good Bot + + Below is a list of actions that can be taken with flex wrapping supported: + + + + + + + + + + + + ); +}; + +render( + +)`.trim(); +export const botWithLoadingStopButton = ` +const MessageWithLoadingAndStop = () => { + return ( + + + + Good Bot + + + {}} /> + + + + ); +}; + +render( + +)`.trim(); +export const botWithLoading = ` +const MessageWithLoading = () => { + return ( + + + + Good Bot + + + + + + + ); +}; + +render( + +)`.trim(); +export const kitchenSink = ` +const AIChatLogExample = () => { + return ( + + + Gibby Radki + + Hi, I'm getting errors codes when sending an SMS. + + + + Good Bot + + Error codes can be returned from various parts of the process. What error codes are you encountering? + + + + + + + + + Good Bot + + Error 21608 means you're trying to send a message from an unverified number. Is your number verified in your Twilio account? + + + + Is this helpful? + + + + + + + + Gibby Radki + + + No, how do I verify it? + + + + + Good Bot + + + {}} /> + + + + ); +}; + +render( + +)`.trim(); +export const aiChatLoggerExample = ` +const aiChatFactory = ([ message, variant, metaLabel, meta ]) => { + const time = new Date(0).toLocaleString( + 'en-US', + { hour: 'numeric', minute: 'numeric', timeZone: 'UTC', hour12: true } + ) + + return { + variant, + content: ( + + {meta} + + {message} + + + ) + } +}; + +const chatTemplates = [ + ["Hello", "user", "You said at ", "Gibby Radki"], + ["Hi there", "bot", "AI said at ", "Good Bot"], + ["Greetings", "user", "You said at ", "Gibby Radki"], + ["Good to meet you", "bot", "AI said at ", "Good Bot"] +]; + +const AIChatLoggerExample = () => { + const [templateIdx, setTemplateIdx] = React.useState(2); + const { aiChats, push, pop, clear } = useAIChatLogger( + aiChatFactory(chatTemplates[0]), + aiChatFactory(chatTemplates[1]) + ); + const [loading, setLoading] = React.useState(false); + + const pushChat = () => { + const template = chatTemplates[templateIdx]; + setTemplateIdx((idx) => ++idx % chatTemplates.length); + const chat = aiChatFactory(template); + + if (template[1] === "bot") { + const id = uid(chat.content); + setLoading(true); + push({ + id, + variant: template[1], + content: ( + + Good Bot + + + + + ), + }); + setTimeout(() => { + pop(id); + setLoading(false); + push(chat); + }, 1000); + } else { + push(chat); + } + } + + const popChat = () => { + pop(); + setTemplateIdx((idx) => idx === 0 ? idx : --idx % chatTemplates.length); + } + + return( + + + + + + + + + ) +} + +render(); +`.trim(); diff --git a/packages/paste-website/src/pages/components/ai-chat-log/api.mdx b/packages/paste-website/src/pages/components/ai-chat-log/api.mdx new file mode 100644 index 0000000000..06d8a5efe2 --- /dev/null +++ b/packages/paste-website/src/pages/components/ai-chat-log/api.mdx @@ -0,0 +1,100 @@ +export const meta = { + title: "AI Chat Log - API", + package: "@twilio-paste/ai-chat-log", + description: + "An AI Chat Log is a collection of Chat components for displaying conversations between a human and an AI bot", + slug: "/components/ai-chat-log/api", +}; + +import Changelog from "@twilio-paste/ai-chat-log/CHANGELOG.md"; // I don't know why this is needed but if you remove it the page fails to render +import packageJson from "@twilio-paste/ai-chat-log/package.json"; + +import { SidebarCategoryRoutes } from "../../../constants"; +import ComponentPageLayout from "../../../layouts/ComponentPageLayout"; +import { getFeature, getNavigationData, getComponentApi } from "../../../utils/api"; + +export default ComponentPageLayout; + +export const getStaticProps = async () => { + const navigationData = await getNavigationData(); + const feature = await getFeature("AI Chat Log"); + const { componentApi, componentApiTocData } = getComponentApi("@twilio-paste/ai-chat-log"); + return { + props: { + data: { + ...packageJson, + ...feature, + }, + componentApi, + mdxHeadings: [...mdxHeadings, ...componentApiTocData], + navigationData, + pageHeaderData: { + categoryRoute: SidebarCategoryRoutes.COMPONENTS, + githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/ai-chat-log", + storybookUrl: "/?path=/story/components-chatlog--example-chat-log", + }, + }, + }; +}; + +## Installation + +```bash +yarn add @twilio-paste/ai-chat-log - or - yarn add @twilio-paste/core +``` + +## Usage + +```jsx +import { + AIChatLog, + AIChatMessage, + AIChatMessageAuthor, + AIChatMessageBody, + AIChatMessageFeedback, + AIChatMessageLoading, + AIChatMessageMeta, +} from "@twilio-paste/core/ai-chat-log"; + +export const Basic = () => { + return ( + + + Gibby Radki + + Hi, I'm getting errors codes when sending an SMS. + + + + Good Bot + + Error codes can be returned from various parts of the process. What error codes are you encountering? + + + + Is this helpful? + + + + + + + + + + + ); +}; +``` + +## Props + + diff --git a/packages/paste-website/src/pages/components/ai-chat-log/changelog.mdx b/packages/paste-website/src/pages/components/ai-chat-log/changelog.mdx new file mode 100644 index 0000000000..b78049f582 --- /dev/null +++ b/packages/paste-website/src/pages/components/ai-chat-log/changelog.mdx @@ -0,0 +1,38 @@ +export const meta = { + title: "AI Chat Log - Components", + package: "@twilio-paste/ai-chat-log", + description: + "An AI Chat Log is a collection of Chat components for displaying conversations between a human and an AI bot.", + slug: "/components/ai-chat-log/changelog", +}; + +import Changelog from "@twilio-paste/ai-chat-log/CHANGELOG.md"; +import packageJson from "@twilio-paste/ai-chat-log/package.json"; + +import { SidebarCategoryRoutes } from "../../../constants"; +import ComponentPageLayout from "../../../layouts/ComponentPageLayout"; +import { getFeature, getNavigationData } from "../../../utils/api"; + +export default ComponentPageLayout; + +export const getStaticProps = async () => { + const navigationData = await getNavigationData(); + const feature = await getFeature("AI Chat Log"); + return { + props: { + data: { + ...packageJson, + ...feature, + }, + navigationData, + mdxHeadings, + pageHeaderData: { + categoryRoute: SidebarCategoryRoutes.COMPONENTS, + githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/ai-chat-log", + storybookUrl: "/?path=/story/components-chatlog--example-chat-log", + }, + }, + }; +}; + + diff --git a/packages/paste-website/src/pages/components/ai-chat-log/index.mdx b/packages/paste-website/src/pages/components/ai-chat-log/index.mdx new file mode 100644 index 0000000000..0e4a120740 --- /dev/null +++ b/packages/paste-website/src/pages/components/ai-chat-log/index.mdx @@ -0,0 +1,340 @@ +export const meta = { + title: "AI Chat Log - Components", + package: "@twilio-paste/ai-chat-log", + description: + "An AI Chat Log is a collection of AI Chat components for displaying conversations between a human and an AI bot.", + slug: "/components/ai-chat-log/", +}; + +import { uid } from "@twilio-paste/uid-library"; +import { HelpText } from "@twilio-paste/help-text"; +import { Button } from "@twilio-paste/button"; +import { ButtonGroup } from "@twilio-paste/button-group"; +import { Stack } from "@twilio-paste/stack"; +import { + AIChatLog, + AIChatMessage, + AIChatMessageAuthor, + AIChatMessageBody, + AIChatMessageLoading, + AIChatLogger, + useAIChatLogger, + AIChatMessageActionCard, + AIChatMessageActionGroup, +} from "@twilio-paste/core/ai-chat-log"; +import { ThumbsUpIcon } from "@twilio-paste/icons/esm/ThumbsUpIcon"; +import { ThumbsDownIcon } from "@twilio-paste/icons/esm/ThumbsDownIcon"; +import { RefreshIcon } from "@twilio-paste/icons/esm/RefreshIcon"; +import { CopyIcon } from "@twilio-paste/icons/esm/CopyIcon"; + +import Changelog from "@twilio-paste/ai-chat-log/CHANGELOG.md"; + +import { SidebarCategoryRoutes } from "../../../constants"; + +import { + basicBotMessage, + basicHumanMessage, + botWithFeedback, + botWithLoadingStopButton, + botWithLoading, + kitchenSink, + aiChatLoggerExample, + botWithBodyActions, +} from "../../../component-examples/AIChatLogExamples"; +import packageJson from "@twilio-paste/ai-chat-log/package.json"; +import ComponentPageLayout from "../../../layouts/ComponentPageLayout"; +import { getFeature, getNavigationData } from "../../../utils/api"; + +export default ComponentPageLayout; + +export const getStaticProps = async () => { + const navigationData = await getNavigationData(); + const feature = await getFeature("AI Chat Log"); + return { + props: { + data: { + ...packageJson, + ...feature, + }, + navigationData, + mdxHeadings, + pageHeaderData: { + categoryRoute: SidebarCategoryRoutes.COMPONENTS, + githubUrl: "https://github.com/twilio-labs/paste/tree/main/packages/paste-core/components/ai-chat-log", + storybookUrl: "/?path=/story/components-chatlog--example-chat-log", + }, + }, + }; +}; + + + {` + + Gibby Radki + + What does the SMS delivery error code 30003 mean? + + + + Good Bot + + Here is what I found, error code 30003 means: The destination phone is unavailable or turned off, or it may be a landline or phone that doesn't support SMS. + + +`} + + +## Guidelines + +### About AI Chat Log + +An AI Chat Log is a way to display conversations between a user and AI. If you are looking for a chat between 2 or more humans please refer to [ChatLog](/components/chat-log). + +The AI Chat Log package includes these main components: + +- AIChatLog +- AIChatMessage +- AIChatMessageAuthor +- AIChatMessageBody +- AIChatMessageActionGroup +- AIChatMessageActionCard +- AIChatMessageLoading + +### Accessibility + +To ensure the chat is accessible, only use the AI Chat components within an `AIChatLog` component and use `AIChatMessage` to wrap `AIChatMessageBody`, `AIChatMessageActionGroup` and components together. + +The only other accessibility requirement is providing the `AIChatMessageActionCard` a descriptive label via the `aria-label` React prop. + +The AIChatLog component has `role="log"` which means that any new messages added to it are announced by assistive technology. + +## Examples + +### Basic Message + +A message must include the author and body. Any message text from a user or a bot must be contained within the `AIChatMessageBody` component. Due to lengthy AI responses, the chat layout is now top-down. + +#### Bot + + + {basicBotMessage} + + +#### User + + + {basicHumanMessage} + + +### Message Body Sizes + +The `AIChatMessageBody` component has two sizes, `size="default"` and `size="fullScreen"`. The fullScreen size is used where the ChatLog is displayed in the full width of the page where larger font size is needed. + + + {` + + Gibby Radki + + I'm a message that should be displayed in compact elements + + + + Gibby Radki + + I'm a message that will be displayed in full screen width + + +`} + + +### Message with Actions + +Message actions can be used to provide quick responses or actions to the user. + +`AIChatMessageActionGroup` should be a child of `AIChatMessage` so that the text and meta information are correctly grouped together for assistive technologies. `AIChatMessageActionCard` also needs a readable `aria-label` that summarizes what the meta information says. + +Each item within `AIChatMessageActionGroup` should be wrapped with `AIChatMessageActionCard`. It is recommended to use reset button variants for content within `AIChatMessageActionCard`. + +Actions can still be added in `AIChatMessageBody` which are returned from the AI response. + +#### Feedback in AIChatMessageActionCard + + + {botWithFeedback} + + +#### Buttons in AIChatMessageBody from AI Response + + + {botWithBodyActions} + + +### Loading States + +Use the `AIChatMessageLoading` component to indicate that the bot is typing or processing a response. During this time **no user input should be accepted**. No new messages should be added to a chat until the AI operation is finished processing. + +The SkeletonLoader lengths vary on each render to give a more natural pending message body interaction. + +#### Loading + + + {botWithLoading} + + +#### Loading with Stop Button + + + {botWithLoadingStopButton} + + +### Example AI Chat Log + +This example combines all the separate features displayed previously into one example. It shows how all the features work together harmoniously through composition. + + + {kitchenSink} + + +### useAIChatLogger hook + +The `useAIChatLogger` hook provides a hook-based approach to managing AI chat state. It is best used with the `` component. + +`useAIChatLogger` returns 4 things: + +- An array of `aiChats`. +- A `push` method used to add a chat, optionally with a custom ID. +- A `pop` method used to remove a chat, optionally via its ID. +- A `clear` method used to remove all chats. + +##### AIChatLogger component + +The `` component handles rendering the chats it is passed via props. It handles how chats enter and leave the UI. + +``` +const { aiChats } = useAIChatLogger(); +return ; +``` + +##### Adding and removing a chat + +You can push or pop a chat based on an action or event. In this example it's based on a button click: + + + {aiChatLoggerExample} + + +## Composition Notes + +Keep any generated responses from the AI contained in the `AIChatMessageBody` component. Each chat message shouls only have one `AIChatMessageBody` component. diff --git a/packages/paste-website/src/pages/components/chat-log/index.mdx b/packages/paste-website/src/pages/components/chat-log/index.mdx index 6638f26bcb..d7f6729504 100644 --- a/packages/paste-website/src/pages/components/chat-log/index.mdx +++ b/packages/paste-website/src/pages/components/chat-log/index.mdx @@ -100,7 +100,7 @@ export const getStaticProps = async () => { ### About Chat Log -A Chat Log is a way to display conversations between people and can include complex content like attachments. The chat can be between two or more people. +A Chat Log is a way to display conversations between people and can include complex content like attachments. The chat can be between two or more people. If you are looking for a chat between a human and an AI please refer to [AIChatLog](/components/ai-chat-log). The Chat Log package includes these main components: @@ -292,7 +292,7 @@ This example combines all the separate features displayed previously into one ex The `useChatLogger` hook provides a hook based approach to managing chat state. It is best used with the `` component. -`useChatLogger` returns 3 things: +`useChatLogger` returns 4 things: - An array of `chats`. - A `push` method used to add a chat, optionally with a custom ID