Skip to content

Commit 5cf1ae5

Browse files
Fix Dropdown change event when allow_custom_value is true (#12572)
* Add code * add changeset * Fix * custom value * Fix * Fix typing issue --------- Co-authored-by: gradio-pr-bot <[email protected]>
1 parent 1724a02 commit 5cf1ae5

File tree

5 files changed

+114
-5
lines changed

5 files changed

+114
-5
lines changed

.changeset/hot-paths-shine.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@gradio/dropdown": patch
3+
"gradio": patch
4+
---
5+
6+
fix:Fix Dropdown change event when allow_custom_value is true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: dropdown_custom_value"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio\n", "\n", "with gradio.Blocks() as demo:\n", " dropdown = gradio.Dropdown(\n", " choices=[(\"hello\", \"goodbye\"), (\"abc\", \"123\")],\n", " allow_custom_value=True,\n", " label=\"Dropdown\",\n", " )\n", " text = gradio.Textbox(label=\"Output\")\n", " dropdown.change(lambda x: x, inputs=dropdown, outputs=text)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}

demo/dropdown_custom_value/run.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import gradio
2+
3+
with gradio.Blocks() as demo:
4+
dropdown = gradio.Dropdown(
5+
choices=[("hello", "goodbye"), ("abc", "123")],
6+
allow_custom_value=True,
7+
label="Dropdown",
8+
)
9+
text = gradio.Textbox(label="Output")
10+
dropdown.change(lambda x: x, inputs=dropdown, outputs=text)
11+
12+
if __name__ == "__main__":
13+
demo.launch()

js/dropdown/shared/Dropdown.svelte

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import { BlockTitle, IconButtonWrapper } from "@gradio/atoms";
44
import { DropdownArrow } from "@gradio/icons";
55
import { handle_filter, handle_shared_keys } from "./utils";
6-
import type {
7-
SelectData,
8-
KeyUpData,
9-
CustomButton as CustomButtonType
6+
import {
7+
type SelectData,
8+
type KeyUpData,
9+
type CustomButton as CustomButtonType
1010
} from "@gradio/utils";
1111
import { tick } from "svelte";
1212
@@ -79,6 +79,9 @@
7979
return ["", null];
8080
}
8181
});
82+
// Use last_typed_value to track when the user has typed
83+
// on_blur we only want to update value if the user has typed
84+
let last_typed_value = input_text;
8285
let initialized = $state(false);
8386
let disabled = $derived(!interactive);
8487
@@ -96,6 +99,7 @@
9699
97100
let [_input_text, _value] = choices[selected_index];
98101
input_text = _input_text;
102+
last_typed_value = input_text;
99103
value = _value;
100104
on_select?.({
101105
index: selected_index,
@@ -104,6 +108,7 @@
104108
});
105109
show_options = false;
106110
active_index = null;
111+
on_input?.();
107112
filter_input.blur();
108113
}
109114
@@ -118,7 +123,13 @@
118123
input_text =
119124
choices_names[choices_values.indexOf(value as string | number)];
120125
} else {
121-
value = input_text;
126+
if (choices_names.includes(input_text)) {
127+
selected_index = choices_names.indexOf(input_text);
128+
value = choices_values[selected_index];
129+
} else if (input_text !== last_typed_value) {
130+
value = input_text;
131+
selected_index = null;
132+
}
122133
}
123134
show_options = false;
124135
active_index = null;
@@ -137,6 +148,7 @@
137148
filtered_indices
138149
);
139150
if (e.key === "Enter") {
151+
last_typed_value = input_text;
140152
if (active_index !== null) {
141153
selected_index = active_index;
142154
value = choices_values[active_index];
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { test, expect } from "@self/tootils";
2+
3+
test.describe("Dropdown with allow_custom_value=True", () => {
4+
test("selecting an option updates the textbox with the value, not the name", async ({
5+
page
6+
}) => {
7+
const dropdown = page.getByRole("listbox", { name: "Dropdown" });
8+
const output = page.getByLabel("Output");
9+
10+
await dropdown.click();
11+
await page.getByRole("option", { name: "abc" }).click();
12+
13+
await expect(output).toHaveValue("123");
14+
});
15+
16+
test("blurring dropdown after selecting an option does not change the value", async ({
17+
page
18+
}) => {
19+
const dropdown = page.getByRole("listbox", { name: "Dropdown" });
20+
const output = page.getByLabel("Output");
21+
22+
await dropdown.click();
23+
await page.getByRole("option", { name: "abc" }).click();
24+
25+
await expect(output).toHaveValue("123");
26+
27+
await dropdown.click();
28+
await dropdown.blur();
29+
30+
await expect(output).toHaveValue("123");
31+
});
32+
33+
test("typing a custom value and pressing Enter updates the textbox", async ({
34+
page
35+
}) => {
36+
const dropdown = page.getByRole("listbox", { name: "Dropdown" });
37+
const output = page.getByLabel("Output");
38+
39+
// Focus the dropdown and type a custom value
40+
await dropdown.click();
41+
await dropdown.fill("my custom value");
42+
await dropdown.press("Enter");
43+
44+
// The textbox should show the custom value
45+
await expect(output).toHaveValue("my custom value");
46+
});
47+
48+
test("typing a custom value and blurring updates the textbox", async ({
49+
page
50+
}) => {
51+
const dropdown = page.getByRole("listbox", { name: "Dropdown" });
52+
const output = page.getByLabel("Output");
53+
54+
await dropdown.click();
55+
await dropdown.fill("another custom value");
56+
await dropdown.blur();
57+
58+
await expect(output).toHaveValue("another custom value");
59+
});
60+
61+
test("selecting an option after typing a custom value works correctly", async ({
62+
page
63+
}) => {
64+
const dropdown = page.getByRole("listbox", { name: "Dropdown" });
65+
const output = page.getByLabel("Output");
66+
67+
await dropdown.click();
68+
await dropdown.fill("custom");
69+
await dropdown.press("Enter");
70+
await expect(output).toHaveValue("custom");
71+
72+
await dropdown.click();
73+
await page.getByRole("option", { name: "hello" }).click();
74+
75+
await expect(output).toHaveValue("goodbye");
76+
});
77+
});

0 commit comments

Comments
 (0)