Skip to content

Commit 0cc6e7a

Browse files
committed
feat: Add style API to ChatBubble component allowing customization
1 parent 4211f2e commit 0cc6e7a

File tree

8 files changed

+554
-7
lines changed

8 files changed

+554
-7
lines changed
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import Box from "@cloudscape-design/components/box";
4+
import Steps from "@cloudscape-design/components/steps";
5+
6+
import { Avatar } from "../../lib/components";
7+
import { ChatBubble } from "../../lib/components";
8+
import { Page } from "../app/templates";
9+
import { TestBed } from "../app/test-bed";
10+
import smiley from "../avatar/smiley.png";
11+
import { Actions, ChatBubbleAvatarGenAI, ChatBubbleAvatarUser, ChatContainer, longText } from "./util-components";
12+
13+
export default function ChatBubblePage() {
14+
// const longText = "This is an example text";
15+
return (
16+
<Page title="Chat bubble">
17+
<TestBed>
18+
<ChatContainer>
19+
{/* Background Color Permutations */}
20+
<ChatBubble
21+
style={{
22+
bubble: {
23+
background: "#f0f0f0",
24+
color: "black",
25+
},
26+
}}
27+
type="outgoing"
28+
avatar={<ChatBubbleAvatarUser />}
29+
ariaLabel="Background color test"
30+
>
31+
{longText}
32+
</ChatBubble>
33+
34+
{/* Border Permutations */}
35+
<ChatBubble
36+
style={{
37+
bubble: {
38+
borderColor: "red",
39+
borderWidth: "10px",
40+
borderRadius: "16px",
41+
},
42+
}}
43+
type="incoming"
44+
avatar={<ChatBubbleAvatarGenAI />}
45+
ariaLabel="Border test"
46+
>
47+
{longText}
48+
</ChatBubble>
49+
50+
{/* Color and Typography */}
51+
<ChatBubble
52+
style={{
53+
bubble: {
54+
color: "purple",
55+
fontSize: "18px",
56+
fontWeight: "bold",
57+
},
58+
}}
59+
type="outgoing"
60+
avatar={<ChatBubbleAvatarUser />}
61+
ariaLabel="Typography test"
62+
>
63+
{longText}
64+
</ChatBubble>
65+
66+
{/* Box Shadow */}
67+
<ChatBubble
68+
style={{
69+
bubble: {
70+
boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
71+
},
72+
}}
73+
type="incoming"
74+
avatar={<ChatBubbleAvatarGenAI />}
75+
ariaLabel="Shadow test"
76+
>
77+
{longText}
78+
</ChatBubble>
79+
80+
{/* Padding Permutations */}
81+
<ChatBubble
82+
style={{
83+
bubble: { background: "lightblue" },
84+
}}
85+
type="outgoing"
86+
avatar={<ChatBubbleAvatarUser />}
87+
ariaLabel="Padding test"
88+
>
89+
{longText}
90+
</ChatBubble>
91+
92+
{/* Combined Styles */}
93+
<ChatBubble
94+
style={{
95+
bubble: {
96+
background: "#ffffff",
97+
color: "#000000",
98+
borderColor: "#000000",
99+
borderWidth: "2px",
100+
borderRadius: "16px",
101+
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
102+
fontSize: "16px",
103+
fontWeight: "500",
104+
},
105+
}}
106+
type="incoming"
107+
avatar={<ChatBubbleAvatarUser />}
108+
ariaLabel="Combined styles test"
109+
>
110+
{"This is an example text!"}
111+
</ChatBubble>
112+
113+
{/* Loading State with Custom Styles */}
114+
<ChatBubble
115+
style={{
116+
bubble: {
117+
// background: "#ecd380ff",
118+
// paddingBlock: "26px",
119+
// paddingInline: "20px",
120+
// borderRadius: "40px",
121+
rowGap: "4px",
122+
},
123+
}}
124+
avatar={<ChatBubbleAvatarGenAI loading={true} />}
125+
type="incoming"
126+
showLoadingBar={true}
127+
ariaLabel="Loading with custom styles"
128+
>
129+
<Box color="text-body-secondary">{`Generating a response (using Box)`}</Box>
130+
</ChatBubble>
131+
132+
{/* Actions with Custom Styles */}
133+
<ChatBubble
134+
style={{
135+
root: {
136+
columnGap: "40px",
137+
},
138+
bubble: {
139+
background: "#e8f5e8",
140+
borderRadius: "20px",
141+
},
142+
}}
143+
avatar={<ChatBubbleAvatarGenAI />}
144+
type="incoming"
145+
showLoadingBar={true}
146+
actions={<Actions />}
147+
ariaLabel="Actions with custom styles"
148+
>
149+
{`This is an example text with styles, and action`}
150+
</ChatBubble>
151+
152+
{/* Minimal Style Override */}
153+
<ChatBubble
154+
style={{ bubble: { borderRadius: "4px" } }}
155+
type="outgoing"
156+
avatar={<ChatBubbleAvatarUser />}
157+
ariaLabel="Minimal override"
158+
>
159+
{longText}
160+
</ChatBubble>
161+
162+
{/* Default (no custom styles) */}
163+
<ChatBubble
164+
type="incoming"
165+
avatar={
166+
<Avatar
167+
color="default"
168+
ariaLabel="AB"
169+
imgUrl={smiley}
170+
style={{
171+
root: {
172+
focusRing: {
173+
borderColor: "green",
174+
borderRadius: "30px",
175+
borderWidth: "4px",
176+
},
177+
},
178+
}}
179+
/>
180+
}
181+
ariaLabel="Default styles"
182+
>
183+
{longText}
184+
</ChatBubble>
185+
<ChatBubble
186+
style={{
187+
root: {
188+
columnGap: "20px",
189+
},
190+
bubble: {
191+
background: "#b3e5f3ff",
192+
color: "#43217eff",
193+
borderColor: "#34d906ff",
194+
borderWidth: "5px",
195+
borderRadius: "16px",
196+
boxShadow: "0 10px 8px rgba(0,0,0,0.15)",
197+
fontSize: "20px",
198+
fontWeight: "700",
199+
},
200+
}}
201+
type="incoming"
202+
avatar={<ChatBubbleAvatarUser />}
203+
ariaLabel="Combined styles test"
204+
>
205+
Steps
206+
<Steps
207+
steps={[
208+
{ status: "success", header: "Success step" },
209+
{ status: "info", header: "Info step" },
210+
{ status: "warning", header: "Warning step" },
211+
{ status: "error", header: "Error step" },
212+
{ status: "loading", header: "Loading step" },
213+
{ status: "in-progress", header: "In progress step" },
214+
{ status: "pending", header: "Pending step" },
215+
{ status: "stopped", header: "Stopped step" },
216+
]}
217+
/>
218+
</ChatBubble>
219+
220+
{/* Loading bar stick test */}
221+
<ChatBubble
222+
type="incoming"
223+
avatar={<ChatBubbleAvatarGenAI />}
224+
showLoadingBar={true}
225+
ariaLabel="Loading bar stick test"
226+
>
227+
{longText}
228+
</ChatBubble>
229+
230+
{/* New comprehensive style component */}
231+
<ChatBubble
232+
style={{
233+
root: {
234+
columnGap: "24px",
235+
},
236+
bubble: {
237+
background: "light-dark(#ffffff, #30403eff)",
238+
color: "light-dark(#1b5e20, #b0e9b0ff)",
239+
borderColor: "light-dark(#4caf50, #66bb6a)",
240+
borderWidth: "1px",
241+
borderRadius: "24px",
242+
boxShadow: "light-dark(0 8px 16px rgba(76, 175, 80, 0.3), 0 8px 16px rgba(102, 187, 106, 0.4))",
243+
fontSize: "17px",
244+
fontWeight: "600",
245+
paddingBlock: "20px",
246+
paddingInline: "30px",
247+
rowGap: "20px",
248+
},
249+
}}
250+
type="outgoing"
251+
avatar={<ChatBubbleAvatarGenAI />}
252+
actions={<Actions />}
253+
ariaLabel="Green-themed light/dark component"
254+
>
255+
This is a new component with comprehensive styling including gradient background, custom spacing, enhanced
256+
typography, and colors.
257+
</ChatBubble>
258+
</ChatContainer>
259+
</TestBed>
260+
</Page>
261+
);
262+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import { useState } from "react";
4+
5+
import Box from "@cloudscape-design/components/box";
6+
import Checkbox from "@cloudscape-design/components/checkbox";
7+
import FormField from "@cloudscape-design/components/form-field";
8+
import Input from "@cloudscape-design/components/input";
9+
import SpaceBetween from "@cloudscape-design/components/space-between";
10+
import Textarea from "@cloudscape-design/components/textarea";
11+
12+
import { ChatBubble } from "../../lib/components";
13+
import { Page } from "../app/templates";
14+
import { TestBed } from "../app/test-bed";
15+
import { Actions, ChatBubbleAvatarGenAI, ChatContainer } from "./util-components";
16+
17+
export default function StylePlaygroundPage() {
18+
const [styleInput, setStyleInput] = useState(`{
19+
"root": {
20+
"columnGap": "25px"
21+
},
22+
"bubble": {
23+
"background": "#ffffff",
24+
"color": "#1b5e20",
25+
"borderColor": "#4caf50",
26+
"borderWidth": "1px",
27+
"borderRadius": "24px",
28+
"boxShadow": "#30403eff",
29+
"fontSize": "17px",
30+
"fontWeight": "600",
31+
"paddingBlock": "20px",
32+
"paddingInline": "30px",
33+
"rowGap": "20px"
34+
}
35+
}`);
36+
const [showLoadingBar, setShowLoadingBar] = useState(false);
37+
const [showAvatar, setShowAvatar] = useState(true);
38+
const [textInput, setTextInput] = useState(
39+
"Hello! This is a sample message to demonstrate the chat bubble styling. Feel free to edit this text to see how your styles look with different content.",
40+
);
41+
42+
let parsedStyle = {};
43+
try {
44+
parsedStyle = JSON.parse(styleInput);
45+
} catch {
46+
// Invalid JSON, use empty object
47+
}
48+
49+
return (
50+
<Page title="Chat Bubble Style Playground">
51+
<TestBed>
52+
<SpaceBetween size="l">
53+
<FormField label="Style Dictionary (JSON)">
54+
<Textarea value={styleInput} onChange={({ detail }) => setStyleInput(detail.value)} rows={15} />
55+
</FormField>
56+
57+
<FormField label="Text">
58+
<Input value={textInput} onChange={({ detail }) => setTextInput(detail.value)} />
59+
</FormField>
60+
<Checkbox checked={showLoadingBar} onChange={({ detail }) => setShowLoadingBar(detail.checked)}>
61+
Show Loading Bar
62+
</Checkbox>
63+
<Checkbox checked={showAvatar} onChange={({ detail }) => setShowAvatar(detail.checked)}>
64+
Show Avatar
65+
</Checkbox>
66+
67+
<Box variant="h2">Preview</Box>
68+
<ChatContainer>
69+
<ChatBubble
70+
style={parsedStyle}
71+
type="incoming"
72+
avatar={showAvatar ? <ChatBubbleAvatarGenAI /> : undefined}
73+
showLoadingBar={showLoadingBar}
74+
actions={<Actions />}
75+
ariaLabel="Style playground preview"
76+
>
77+
{textInput}
78+
</ChatBubble>
79+
</ChatContainer>
80+
</SpaceBetween>
81+
</TestBed>
82+
</Page>
83+
);
84+
}

0 commit comments

Comments
 (0)