Skip to content

Commit

Permalink
Improve chatbot accessibility and UX (#5699)
Browse files Browse the repository at this point in the history
* add a11y changes and css tweaks

* add a11y changes and css tweaks

* change like/dislike/copy buttons ux

* cleanup

* add laout param

* tweak

* add changeset

* fill icon on click

* text alignment tweak

* format + test

* fix browser test

* avatar tweaks

* add stories

* tweak

* tweak

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
hannahblair and gradio-pr-bot committed Oct 2, 2023
1 parent 546e1d8 commit 8f0fed8
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 271 deletions.
7 changes: 7 additions & 0 deletions .changeset/public-impalas-sin.md
@@ -0,0 +1,7 @@
---
"@gradio/chatbot": minor
"@gradio/icons": minor
"gradio": minor
---

feat:Improve chatbot accessibility and UX
5 changes: 5 additions & 0 deletions gradio/components/chatbot.py
Expand Up @@ -60,6 +60,7 @@ def __init__(
sanitize_html: bool = True,
render_markdown: bool = True,
bubble_full_width: bool = True,
layout: Literal["panel", "bubble"] | None = None,
**kwargs,
):
"""
Expand All @@ -84,6 +85,7 @@ def __init__(
sanitize_html: If False, will disable HTML sanitization for chatbot messages. This is not recommended, as it can lead to security vulnerabilities.
render_markdown: If False, will disable Markdown rendering for chatbot messages.
bubble_full_width: If False, the chat bubble will fit to the content of the message. If True (default), the chat bubble will be the full width of the component.
layout: If "panel", will display the chatbot in a llm style layout. If "bubble", will display the chatbot with message bubbles, with the user and bot messages on alterating sides. Will default to "bubble".
"""
if color_map is not None:
warn_deprecation("The 'color_map' parameter has been deprecated.")
Expand Down Expand Up @@ -114,6 +116,7 @@ def __init__(
self.show_copy_button = show_copy_button
self.sanitize_html = sanitize_html
self.bubble_full_width = bubble_full_width
self.layout = layout
IOComponent.__init__(
self,
label=label,
Expand Down Expand Up @@ -148,6 +151,7 @@ def update(
avatar_images: tuple[str | Path | None] | None = None,
sanitize_html: bool | None = None,
bubble_full_width: bool | None = None,
layout: Literal["panel", "bubble"] | None = None,
render_markdown: bool | None = None,
):
warnings.warn(
Expand All @@ -170,6 +174,7 @@ def update(
"sanitize_html": sanitize_html,
"bubble_full_width": bubble_full_width,
"render_markdown": render_markdown,
"layout": layout,
"__type__": "update",
}
return updated_config
Expand Down
24 changes: 12 additions & 12 deletions js/app/test/chatinterface_streaming_echo.spec.ts
Expand Up @@ -3,11 +3,11 @@ import { test, expect } from "@gradio/tootils";
test("chatinterface works with streaming functions and all buttons behave as expected", async ({
page
}) => {
const submit_button = await page.locator("button").nth(0);
const retry_button = await page.locator("button").nth(2);
const undo_button = await page.locator("button").nth(3);
const clear_button = await page.locator("button").nth(4);
const textbox = await page.getByTestId("textbox").nth(0);
const submit_button = await page.getByRole("button", { name: "Submit" });
const retry_button = await page.getByRole("button", { name: "🔄 Retry" });
const undo_button = await page.getByRole("button", { name: "↩️ Undo" });
const clear_button = await page.getByRole("button", { name: "🗑️ Clear" });
const textbox = await page.getByPlaceholder("Type a message...");

let iterations: Promise<any>[] = [];
page.on("websocket", (ws) => {
Expand All @@ -26,16 +26,16 @@ test("chatinterface works with streaming functions and all buttons behave as exp
await submit_button.click();
await iterations[0];
await expect(textbox).toHaveValue("");
await expect.poll(async () => page.locator(".bot.message p").count()).toBe(1);
const bot_message_0 = await page.locator(".bot.message p").nth(0);
await expect.poll(async () => page.locator(".chatbot p").count()).toBe(1);
const bot_message_0 = await page.locator(".bot.message").nth(0);
await expect(bot_message_0).toContainText("You typed: hello");

await textbox.fill("hi");
await submit_button.click();
await iterations[1];
await expect(textbox).toHaveValue("");
await expect.poll(async () => page.locator(".bot.message p").count()).toBe(2);
const bot_message_1 = await page.locator(".bot.message p").nth(1);
await expect.poll(async () => page.locator(".message.bot").count()).toBe(2);
const bot_message_1 = await page.locator(".bot").nth(1);
await expect(bot_message_1).toContainText("You typed: hi");

await retry_button.click();
Expand All @@ -45,16 +45,16 @@ test("chatinterface works with streaming functions and all buttons behave as exp

await undo_button.click();
await iterations[3];
await expect.poll(async () => page.locator(".bot.message p").count()).toBe(1);
await expect.poll(async () => page.locator(".message.bot").count()).toBe(1);
await expect(textbox).toHaveValue("hi");

await textbox.fill("salaam");
await submit_button.click();
await iterations[4];
await expect(textbox).toHaveValue("");
await expect.poll(async () => page.locator(".bot.message p").count()).toBe(2);
await expect.poll(async () => page.locator(".bot.message").count()).toBe(2);
await expect(bot_message_1).toContainText("You typed: salaam");

await clear_button.click();
await expect.poll(async () => page.locator(".bot.message p").count()).toBe(0);
await expect.poll(async () => page.locator(".bot.message").count()).toBe(0);
});
49 changes: 40 additions & 9 deletions js/chatbot/Chatbot.stories.svelte
Expand Up @@ -10,20 +10,20 @@
label: {
control: "text",
description: "The textbox label",
name: "label"
name: "label",
},
show_label: {
options: [true, false],
description: "Whether to show the label",
control: { type: "boolean" },
defaultValue: true
defaultValue: true,
},
rtl: {
options: [true, false],
description: "Whether to render right-to-left",
control: { type: "boolean" },
defaultValue: false
}
defaultValue: false,
},
}}
/>

Expand All @@ -33,10 +33,10 @@
value={[
[
"Can you write a function in Python?",
"```py\ndef test():\n\tprint(x)\n```"
"```py\ndef test():\n\tprint(x)\n```",
],
["Can you do math?", "$$1+1=2$$"],
["Can you say nothing?", null]
["Can you say nothing?", null],
]}
/>
</Template>
Expand All @@ -55,7 +55,7 @@
name="Chatbot with text rendered right-to-left"
args={{
rtl: true,
latex_delimiters: [{ left: "$$", right: "$$", display: true }]
latex_delimiters: [{ left: "$$", right: "$$", display: true }],
}}
/>

Expand All @@ -64,13 +64,44 @@
args={{
rtl: true,
latex_delimiters: [{ left: "$$", right: "$$", display: true }],
show_copy_button: true
show_copy_button: true,
}}
/>

<Story
name="Chatbot with chat bubble full width disabled"
args={{
bubble_full_width: false
bubble_full_width: false,
}}
/>

<Story
name="Chatbot with panel layout enabled"
args={{
bubble_full_width: false,
layout: "panel",
}}
/>

<Story
name="Chatbot with panel layout enabled and avatars"
args={{
layout: "panel",
avatar_images: [
"https://avatars.githubusercontent.com/u/100000?v=4",
"https://avatars.githubusercontent.com/u/100000?v=4",
],
}}
/>

<Story
name="Chatbot with bubble layout enabled and avatars"
args={{
bubble_full_width: true,
layout: "bubble",
avatar_images: [
"https://avatars.githubusercontent.com/u/100000?v=4",
"https://avatars.githubusercontent.com/u/100000?v=4",
],
}}
/>

0 comments on commit 8f0fed8

Please sign in to comment.