Skip to content

Commit

Permalink
Fixes to the .key_up() method to make it usable for a dynamic dropd…
Browse files Browse the repository at this point in the history
…own autocomplete (#7425)

* fixes

* changes

* add changeset

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Hannah <hannahblair@users.noreply.github.com>
  • Loading branch information
3 people committed Feb 16, 2024
1 parent ba747ad commit 3e4e680
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .changeset/witty-news-film.md
@@ -0,0 +1,6 @@
---
"@gradio/dropdown": patch
"gradio": patch
---

feat:Fixes to the `.key_up()` method to make it usable for a dynamic dropdown autocomplete
4 changes: 2 additions & 2 deletions gradio/blocks.py
Expand Up @@ -848,7 +848,7 @@ def set_event_trigger(
collects_event_data: whether to collect event data for this event
trigger_after: if set, this event will be triggered after 'trigger_after' function index
trigger_only_on_success: if True, this event will only be triggered if the previous event was successful (only applies if `trigger_after` is set)
trigger_mode: If "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` event) would allow a second submission after the pending event is complete.
trigger_mode: If "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` and `.key_up()` events) would allow a second submission after the pending event is complete.
concurrency_limit: If set, this is the maximum number of this event that can be running simultaneously. Can be set to None to mean no concurrency_limit (any number of this event can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `queue()`, which itself is 1 by default).
concurrency_id: If set, this is the id of the concurrency group. Events with the same concurrency_id will be limited by the lowest set concurrency_limit.
show_api: whether to show this event in the "view API" page of the Gradio app, or in the ".view_api()" method of the Gradio clients. Unlike setting api_name to False, setting show_api to False will still allow downstream apps to use this event. If fn is None, show_api will automatically be set to False.
Expand Down Expand Up @@ -901,7 +901,7 @@ def set_event_trigger(
"Cannot set a value for `concurrency_limit` with `every`."
)

if _targets[0][1] == "change" and trigger_mode is None:
if _targets[0][1] in ["change", "key_up"] and trigger_mode is None:
trigger_mode = "always_last"
elif trigger_mode is None:
trigger_mode = "once"
Expand Down
2 changes: 1 addition & 1 deletion gradio/component_meta.py
Expand Up @@ -50,7 +50,7 @@ def {{ event }}(self,
postprocess: If False, will not run postprocessing of component data before returning 'fn' output to the browser.
cancels: A list of other events to cancel when this listener is triggered. For example, setting cancels=[click_event] will cancel the click_event, where click_event is the return value of another components .click method. Functions that have not yet run (or generators that are iterating) will be cancelled, but functions that are currently running will be allowed to finish.
every: Run this event 'every' number of seconds while the client connection is open. Interpreted in seconds.
trigger_mode: If "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` event) would allow a second submission after the pending event is complete.
trigger_mode: If "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` and `.key_up()` events) would allow a second submission after the pending event is complete.
js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components.
concurrency_limit: If set, this is the maximum number of this event that can be running simultaneously. Can be set to None to mean no concurrency_limit (any number of this event can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `Blocks.queue()`, which itself is 1 by default).
concurrency_id: If set, this is the id of the concurrency group. Events with the same concurrency_id will be limited by the lowest set concurrency_limit.
Expand Down
2 changes: 1 addition & 1 deletion gradio/events.py
Expand Up @@ -240,7 +240,7 @@ def event_trigger(
postprocess: If False, will not run postprocessing of component data before returning 'fn' output to the browser.
cancels: A list of other events to cancel when this listener is triggered. For example, setting cancels=[click_event] will cancel the click_event, where click_event is the return value of another components .click method. Functions that have not yet run (or generators that are iterating) will be cancelled, but functions that are currently running will be allowed to finish.
every: Run this event 'every' number of seconds while the client connection is open. Interpreted in seconds.
trigger_mode: If "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` event) would allow a second submission after the pending event is complete.
trigger_mode: If "once" (default for all events except `.change()`) would not allow any submissions while an event is pending. If set to "multiple", unlimited submissions are allowed while pending, and "always_last" (default for `.change()` and `.key_up()` events) would allow a second submission after the pending event is complete.
js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components.
concurrency_limit: If set, this is the maximum number of this event that can be running simultaneously. Can be set to None to mean no concurrency_limit (any number of this event can be running simultaneously). Set to "default" to use the default concurrency limit (defined by the `default_concurrency_limit` parameter in `Blocks.queue()`, which itself is 1 by default).
concurrency_id: If set, this is the id of the concurrency group. Events with the same concurrency_id will be limited by the lowest set concurrency_limit.
Expand Down
35 changes: 32 additions & 3 deletions js/dropdown/dropdown.test.ts
Expand Up @@ -410,7 +410,7 @@ describe("Dropdown", () => {
expect(item.value).toBe("apple");
});

test("updating choices should keep the dropdown focus-able and change the choice name", async () => {
test("updating choices should keep the dropdown focus-able and change the value appropriately if custom values are not allowed", async () => {
const { getByLabelText, component } = await render(Dropdown, {
show_label: true,
loading_status,
Expand Down Expand Up @@ -443,6 +443,37 @@ describe("Dropdown", () => {
await expect(item.value).toBe("apple_new_choice");
});

test("updating choices should not reset the value if custom values are allowed", async () => {
const { getByLabelText, component } = await render(Dropdown, {
show_label: true,
loading_status,
value: "apple_internal_value",
allow_custom_value: true,
label: "Dropdown",
choices: [
["apple_choice", "apple_internal_value"],
["zebra_choice", "zebra_internal_value"]
],
filterable: true,
interactive: true
});

const item: HTMLInputElement = getByLabelText(
"Dropdown"
) as HTMLInputElement;

await expect(item.value).toBe("apple_choice");

component.$set({
choices: [
["apple_new_choice", "apple_internal_value"],
["zebra_new_choice", "zebra_internal_value"]
]
});

await expect(item.value).toBe("apple_choice");
});

test("ensure dropdown can have an empty value", async () => {
const { getByLabelText } = await render(Dropdown, {
show_label: true,
Expand All @@ -456,11 +487,9 @@ describe("Dropdown", () => {
filterable: true,
interactive: true
});

const item: HTMLInputElement = getByLabelText(
"Dropdown"
) as HTMLInputElement;

await expect(item.value).toBe("");
});
});
13 changes: 11 additions & 2 deletions js/dropdown/shared/Dropdown.svelte
Expand Up @@ -55,6 +55,7 @@
[input_text, old_value] = choices[selected_index];
old_input_text = input_text;
}
set_input_text();
} else if (choices.length > 0) {
old_selected_index = 0;
selected_index = 0;
Expand Down Expand Up @@ -87,19 +88,26 @@
}
}
$: {
function set_choice_names_values(): void {
choices_names = choices.map((c) => c[0]);
choices_values = choices.map((c) => c[1]);
}
$: choices, set_choice_names_values();
$: {
if (choices !== old_choices) {
set_input_text();
if (!allow_custom_value) {
set_input_text();
}
old_choices = choices;
filtered_indices = handle_filter(choices, input_text);
if (!allow_custom_value && filtered_indices.length > 0) {
active_index = filtered_indices[0];
}
if (filter_input == document.activeElement) {
show_options = true;
}
}
}
Expand All @@ -114,6 +122,7 @@
}
function set_input_text(): void {
set_choice_names_values();
if (value === undefined) {
input_text = "";
selected_index = null;
Expand Down

0 comments on commit 3e4e680

Please sign in to comment.