Skip to content

Commit

Permalink
Add .key_up event listener to gr.Dropdown() (#7404)
Browse files Browse the repository at this point in the history
* dropdown choice bug

* add changeset

* add changeset

* changes

* add changeset

* format

* key down:

* change

* change

* format

* add KeyDownData

* changes

* add demo

* notebook

* add changeset

* key up

* notebook

* changes

* Delete .changeset/shaggy-hairs-peel.md

* Delete .changeset/tasty-spies-spend.md

* revert changeset deletion

* change

* fix unit test

* type disable

* fix

* cset

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
abidlabs and gradio-pr-bot committed Feb 14, 2024
1 parent 65437ce commit 065c5b1
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 10 deletions.
8 changes: 8 additions & 0 deletions .changeset/metal-moons-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@gradio/app": minor
"@gradio/dropdown": minor
"@gradio/utils": minor
"gradio": minor
---

fix:Add `.key_up` event listener to `gr.Dropdown()`
1 change: 1 addition & 0 deletions demo/dropdown_key_up/run.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: dropdown_key_up"]}, {"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 as gr\n", "\n", "def test(value, key_up_data: gr.KeyUpData):\n", " return {\n", " \"component value\": value,\n", " \"input value\": key_up_data.input_value,\n", " \"key\": key_up_data.key\n", " }\n", "\n", "with gr.Blocks() as demo:\n", " d = gr.Dropdown([\"abc\", \"def\"], allow_custom_value=True)\n", " t = gr.JSON()\n", " d.key_up(test, d, t)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
16 changes: 16 additions & 0 deletions demo/dropdown_key_up/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import gradio as gr

def test(value, key_up_data: gr.KeyUpData):
return {
"component value": value,
"input value": key_up_data.input_value,
"key": key_up_data.key
}

with gr.Blocks() as demo:
d = gr.Dropdown(["abc", "def"], allow_custom_value=True)
t = gr.JSON()
d.key_up(test, d, t)

if __name__ == "__main__":
demo.launch()
2 changes: 1 addition & 1 deletion gradio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
from gradio.components.audio import WaveformOptions
from gradio.components.image_editor import Brush, Eraser
from gradio.data_classes import FileData
from gradio.events import EventData, LikeData, SelectData, on
from gradio.events import EventData, KeyUpData, LikeData, SelectData, on
from gradio.exceptions import Error
from gradio.external import load
from gradio.flagging import (
Expand Down
9 changes: 8 additions & 1 deletion gradio/components/dropdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,14 @@ class Dropdown(FormComponent):
Demos: sentence_builder, titanic_survival
"""

EVENTS = [Events.change, Events.input, Events.select, Events.focus, Events.blur]
EVENTS = [
Events.change,
Events.input,
Events.select,
Events.focus,
Events.blur,
Events.key_up,
]

def __init__(
self,
Expand Down
19 changes: 19 additions & 0 deletions gradio/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,21 @@ def __init__(self, target: Block | None, data: Any):
"""


class KeyUpData(EventData):
def __init__(self, target: Block | None, data: Any):
super().__init__(target, data)
self.key: str = data["key"]
"""
The key that was pressed.
"""
self.input_value: str = data["input_value"]
"""
The displayed value in the input textbox after the key was pressed. This may be different than the `value`
attribute of the component itself, as the `value` attribute of some components (e.g. Dropdown) are not updated
until the user presses Enter.
"""


@dataclasses.dataclass
class EventListenerMethod:
block: Block | None
Expand Down Expand Up @@ -516,6 +531,10 @@ class Events:
"load",
doc="This listener is triggered when the {{ component }} initially loads in the browser.",
)
key_up = EventListener(
"key_up",
doc="This listener is triggered when the user presses a key while the {{ component }} is focused.",
)


class LikeData(EventData):
Expand Down
2 changes: 1 addition & 1 deletion js/app/src/Blocks.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@
if (
instance_map[id].props.interactive &&
status.stage === "pending" &&
dep.targets[0][1] !== "focus"
!["focus", "key_up"].includes(dep.targets[0][1])
) {
pending_outputs.push(id);
instance_map[id].props.interactive = false;
Expand Down
5 changes: 4 additions & 1 deletion js/dropdown/Index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</script>

<script lang="ts">
import type { Gradio, SelectData } from "@gradio/utils";
import type { Gradio, KeyUpData, SelectData } from "@gradio/utils";
import Multiselect from "./shared/Multiselect.svelte";
import Dropdown from "./shared/Dropdown.svelte";
import { Block } from "@gradio/atoms";
Expand Down Expand Up @@ -35,6 +35,7 @@
select: SelectData;
blur: never;
focus: never;
key_up: KeyUpData;
}>;
export let interactive: boolean;
</script>
Expand Down Expand Up @@ -72,6 +73,7 @@
on:select={(e) => gradio.dispatch("select", e.detail)}
on:blur={() => gradio.dispatch("blur")}
on:focus={() => gradio.dispatch("focus")}
on:key_up={() => gradio.dispatch("key_up")}
disabled={!interactive}
/>
{:else}
Expand All @@ -90,6 +92,7 @@
on:select={(e) => gradio.dispatch("select", e.detail)}
on:blur={() => gradio.dispatch("blur")}
on:focus={() => gradio.dispatch("focus")}
on:key_up={(e) => gradio.dispatch("key_up", e.detail)}
disabled={!interactive}
/>
{/if}
Expand Down
12 changes: 8 additions & 4 deletions js/dropdown/dropdown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ describe("Dropdown", () => {
expect(options[1]).toContainHTML("name2");
});

test("editing the textbox value should filter the options", async () => {
const { getByLabelText, getAllByTestId } = await render(Dropdown, {
test("editing the textbox value should trigger the type event and filter the options", async () => {
const { getByLabelText, listen, getAllByTestId } = await render(Dropdown, {
show_label: true,
loading_status,
max_choices: 10,
Expand All @@ -89,6 +89,8 @@ describe("Dropdown", () => {
interactive: true
});

const key_up_event = listen("key_up");

const item: HTMLInputElement = getByLabelText(
"Dropdown"
) as HTMLInputElement;
Expand All @@ -100,10 +102,12 @@ describe("Dropdown", () => {

item.value = "";
await event.keyboard("z");

const options_new = getAllByTestId("dropdown-option");

expect(options_new).toHaveLength(1);
expect(options[0]).toContainHTML("zebra");
await expect(options_new).toHaveLength(1);
await expect(options[0]).toContainHTML("zebra");
await assert.equal(key_up_event.callCount, 1);
});

test("blurring the textbox should cancel the filter", async () => {
Expand Down
8 changes: 7 additions & 1 deletion js/dropdown/shared/Dropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { createEventDispatcher, afterUpdate } from "svelte";
import { BlockTitle } from "@gradio/atoms";
import { DropdownArrow } from "@gradio/icons";
import type { SelectData } from "@gradio/utils";
import type { SelectData, KeyUpData } from "@gradio/utils";
import { handle_filter, handle_change, handle_shared_keys } from "./utils";
export let label: string;
Expand Down Expand Up @@ -42,6 +42,7 @@
select: SelectData;
blur: undefined;
focus: undefined;
key_up: KeyUpData;
}>();
// Setting the initial value of the dropdown
Expand Down Expand Up @@ -211,6 +212,11 @@
bind:value={input_text}
bind:this={filter_input}
on:keydown={handle_key_down}
on:keyup={(e) =>
dispatch("key_up", {
key: e.key,
input_value: input_text
})}
on:blur={handle_blur}
on:focus={handle_focus}
readonly={!filterable}
Expand Down
8 changes: 7 additions & 1 deletion js/dropdown/shared/Multiselect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { _, number } from "svelte-i18n";
import { BlockTitle } from "@gradio/atoms";
import { Remove, DropdownArrow } from "@gradio/icons";
import type { SelectData, I18nFormatter } from "@gradio/utils";
import type { KeyUpData, SelectData, I18nFormatter } from "@gradio/utils";
import DropdownOptions from "./DropdownOptions.svelte";
import { handle_filter, handle_change, handle_shared_keys } from "./utils";
Expand Down Expand Up @@ -42,6 +42,7 @@
select: SelectData;
blur: undefined;
focus: undefined;
key_up: KeyUpData;
}>();
// Setting the initial value of the multiselect dropdown
Expand Down Expand Up @@ -259,6 +260,11 @@
bind:value={input_text}
bind:this={filter_input}
on:keydown={handle_key_down}
on:keyup={(e) =>
dispatch("key_up", {
key: e.key,
input_value: input_text
})}
on:blur={handle_blur}
on:focus={handle_focus}
readonly={!filterable}
Expand Down
5 changes: 5 additions & 0 deletions js/utils/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface LikeData {
liked?: boolean;
}

export interface KeyUpData {
key: string;
input_value: string;
}

export interface ShareData {
description: string;
title?: string;
Expand Down

0 comments on commit 065c5b1

Please sign in to comment.