Skip to content

Commit

Permalink
feat: Chat bubble (#21)
Browse files Browse the repository at this point in the history
* feat: First iteration prototype of chat bubbles

* chore: Update simple page and add bubble settings

* chore: Add test util and test

* fix: Chat bubble grey backgroundcolor

* add checkbox to set role to log

* fix: test

* update documenter

* fix documenter snapshot

* add checkbox to focus on container

* Add checkbox to focus on bubble

* add loading bar

* update documenter

* make container focus default

* update test

* a11y suggestions and design sign-off changes

* updated API

* update API and implementation

* comment out code view in dev page and add chat bubble export

* fix styles import in test

* remove code-view from package.json

* update api and documenter

* update chat bubble test

* update tests

* address comments and change 'type' values

* fix test util function

* add with-updates page

* update with-updates page

* use design tokens for spacing

* add temporary dark mode background color

* use chat bubble design tokens

* address comments

* update avatat API description

* move loading bar to bottom and remove cleanup from tests

* change inert implementation

* fix test after inert change

* change isGeneratingContent to showLoadingBar API

* update API descriptions to add links
  • Loading branch information
cansuaa authored Oct 25, 2024
1 parent aa807de commit 1e827d5
Show file tree
Hide file tree
Showing 14 changed files with 852 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"exports": {
".": "./index.js",
"./avatar": "./avatar/index.js",
"./chat-bubble": "./chat-bubble/index.js",
"./loading-bar": "./loading-bar/index.js",
"./test-utils/dom": "./test-utils/dom/index.js",
"./test-utils/selectors": "./test-utils/selectors/index.js",
Expand Down
242 changes: 242 additions & 0 deletions pages/chat-bubble/content-variants.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import BarChart from "@cloudscape-design/components/bar-chart";
import Box from "@cloudscape-design/components/box";
import ExpandableSection from "@cloudscape-design/components/expandable-section";
import Link from "@cloudscape-design/components/link";
import Steps from "@cloudscape-design/components/steps";
import Table, { TableProps } from "@cloudscape-design/components/table";

import { ChatBubble } from "../../lib/components";
import { Actions, ChatBubbleAvatarGenAI, ChatContainer } from "./util-components";

export default function ChatBubblesContentVariantsPage() {
return (
<>
<h1>Content variants in chat bubble</h1>

<ChatContainer>
<GenAIChatBubble>
Expandable section and list
<ol>
<li>In the Buckets list, choose the name of the bucket that you want.</li>
<li>Choose Properties.</li>
<li>Navigate to S3.</li>
</ol>
<ExpandableSection headerText="Sources" defaultExpanded={true}>
<div>
<Link href="https://cloudscape.aws.dev/components/link?tabId=playground&example=external-link">
Example link
</Link>
</div>
<div>
<Link href="https://cloudscape.aws.dev/components/link?tabId=playground&example=external-link">
Example link 2
</Link>
</div>
</ExpandableSection>
</GenAIChatBubble>

<GenAIChatBubble>
<span>Table with default variant</span>

<ChatBubbleTable />
</GenAIChatBubble>

<GenAIChatBubble>
<span>Bar chart</span>

<ChatBubbleChart />
</GenAIChatBubble>

<GenAIChatBubble>
<span>Steps</span>

<Steps
steps={[
{ status: "success", header: "Success step" },
{ status: "info", header: "Info step" },
{ status: "warning", header: "Warning step" },
{ status: "error", header: "Error step" },
{ status: "loading", header: "Loading step" },
{ status: "in-progress", header: "In progress step" },
{ status: "pending", header: "Pending step" },
{ status: "stopped", header: "Stopped step" },
]}
/>
</GenAIChatBubble>
</ChatContainer>
</>
);
}

function GenAIChatBubble({ children }: { children: React.ReactNode }) {
return (
<ChatBubble avatar={<ChatBubbleAvatarGenAI />} type="incoming" actions={<Actions />} ariaLabel="Message">
{children}
</ChatBubble>
);
}

function ChatBubbleTable({ variant }: { variant?: TableProps.Variant }) {
return (
<Table
variant={variant}
renderAriaLive={({ firstIndex, lastIndex, totalItemsCount }) =>
`Displaying items ${firstIndex} to ${lastIndex} of ${totalItemsCount}`
}
columnDefinitions={[
{
id: "variable",
header: "Variable name",
cell: (item) => <Link href="#">{item.name || "-"}</Link>,
isRowHeader: true,
},
{
id: "alt",
header: "Text value",
cell: (item) => item.alt || "-",
},
{
id: "description",
header: "Description",
cell: (item) => item.description || "-",
},
]}
enableKeyboardNavigation={true}
items={[
{
name: "Item 1",
alt: "First",
description: "This is the first item",
type: "1A",
size: "Small",
},
{
name: "Item 2",
alt: "Second",
description: "This is the second item",
type: "1B",
size: "Large",
},
{
name: "Item 3",
alt: "Third",
description: "-",
type: "1A",
size: "Large",
},
{
name: "Item 4",
alt: "Fourth",
description: "This is the fourth item",
type: "2A",
size: "Small",
},
{
name: "Item 5",
alt: "-",
description: "This is the fifth item with a longer description",
type: "2A",
size: "Large",
},
{
name: "Item 6",
alt: "Sixth",
description: "This is the sixth item",
type: "1A",
size: "Small",
},
]}
sortingDisabled={true}
/>
);
}

function ChatBubbleChart() {
return (
<BarChart
series={[
{
title: "Site 1",
type: "bar",
data: [
{ x: new Date(1601071200000), y: 34503 },
{ x: new Date(1601078400000), y: 25832 },
{ x: new Date(1601085600000), y: 4012 },
{ x: new Date(1601092800000), y: -5602 },
{ x: new Date(1601100000000), y: 17839 },
],
valueFormatter: (e) =>
"$" +
e.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}),
},
{
title: "Average revenue",
type: "threshold",
y: 19104,
valueFormatter: (e) =>
"$" +
e.toLocaleString("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}),
},
]}
xDomain={[
new Date(1601071200000),
new Date(1601078400000),
new Date(1601085600000),
new Date(1601092800000),
new Date(1601100000000),
]}
yDomain={[-10000, 40000]}
i18nStrings={{
xTickFormatter: (e) =>
e
.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
hour: "numeric",
minute: "numeric",
hour12: !1,
})
.split(",")
.join("\n"),
yTickFormatter: function s(e) {
return Math.abs(e) >= 1e9
? (e / 1e9).toFixed(1).replace(/\.0$/, "") + "G"
: Math.abs(e) >= 1e6
? (e / 1e6).toFixed(1).replace(/\.0$/, "") + "M"
: Math.abs(e) >= 1e3
? (e / 1e3).toFixed(1).replace(/\.0$/, "") + "K"
: e.toFixed(2);
},
}}
ariaLabel="Bar chart"
height={300}
xTitle="Time (UTC)"
yTitle="Revenue (USD)"
empty={
<Box textAlign="center" color="inherit">
<b>No data available</b>
<Box variant="p" color="inherit">
There is no data available
</Box>
</Box>
}
noMatch={
<Box textAlign="center" color="inherit">
<b>No matching data</b>
<Box variant="p" color="inherit">
There is no matching data to display
</Box>
</Box>
}
/>
);
}
70 changes: 70 additions & 0 deletions pages/chat-bubble/simple.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import Box from "@cloudscape-design/components/box";

import { ChatBubble } from "../../lib/components";
import { TestBed } from "../app/test-bed";
import { ScreenshotArea } from "../screenshot-area";
import { Actions, ChatBubbleAvatarGenAI, ChatBubbleAvatarUser, ChatContainer, longText } from "./util-components";

export default function ChatBubblePage() {
return (
<ScreenshotArea>
<h1>Chat bubble</h1>

<TestBed>
<ChatContainer>
<ChatBubble type="outgoing" avatar={<ChatBubbleAvatarUser />} ariaLabel="User at 4:23:20pm">
What can I do with Amazon S3?
</ChatBubble>
<ChatBubble avatar={<ChatBubbleAvatarGenAI />} type="incoming" ariaLabel="Gen AI at at 4:23:23pm">
Amazon S3 provides a simple web service interface that you can use to store and retrieve any amount of data,
at any time, from anywhere.
</ChatBubble>

<ChatBubble type="outgoing" avatar={<ChatBubbleAvatarUser />} ariaLabel="User at 4:25:00pm">
Long text. {longText}
</ChatBubble>

<ChatBubble
avatar={<ChatBubbleAvatarGenAI />}
type="incoming"
actions={<Actions />}
ariaLabel="Gen AI at 4:25:05pm"
>
Long text. {longText}
</ChatBubble>
<ChatBubble
avatar={<ChatBubbleAvatarGenAI />}
type="incoming"
actions={<Actions />}
ariaLabel="Gen AI at 4:25:07pm"
hideAvatar={true}
>
Second consecutive message coming from the same author, avatar is hidden.
</ChatBubble>

<ChatBubble
avatar={<ChatBubbleAvatarGenAI loading={true} />}
type="incoming"
showLoadingBar={true}
ariaLabel="Gen AI at 4:24:24pm"
>
<Box color="text-body-secondary">Generating a response (using Box)</Box>
</ChatBubble>

<ChatBubble
avatar={<ChatBubbleAvatarGenAI loading={true} />}
type="incoming"
showLoadingBar={true}
actions={<Actions />}
ariaLabel="Gen AI at 4:24:25pm"
>
Generating a response with actions
</ChatBubble>
</ChatContainer>
</TestBed>
</ScreenshotArea>
);
}
Loading

0 comments on commit 1e827d5

Please sign in to comment.