diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7c972d2cb..58f410d932 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
# Upcoming Release
## New Features:
-No changes to highlight.
+* Add support for `'password'` and `'email'` types to `Textbox`. [@pngwn](https://github.com/pngwn) in [PR 2653](https://github.com/gradio-app/gradio/pull/2653)
## Bug Fixes:
* Updated the minimum FastApi used in tests to version 0.87 [@freddyaboulton](https://github.com/freddyaboulton) in [PR 2647](https://github.com/gradio-app/gradio/pull/2647)
diff --git a/demo/blocks_inputs/run.py b/demo/blocks_inputs/run.py
index 502b1ed914..b259312d8e 100644
--- a/demo/blocks_inputs/run.py
+++ b/demo/blocks_inputs/run.py
@@ -1,36 +1,46 @@
import gradio as gr
import os
+
def combine(a, b):
return a + " " + b
+
def mirror(x):
return x
+
with gr.Blocks() as demo:
-
+
txt = gr.Textbox(label="Input", lines=2)
txt_2 = gr.Textbox(label="Input 2")
txt_3 = gr.Textbox(value="", label="Output")
btn = gr.Button(value="Submit")
btn.click(combine, inputs=[txt, txt_2], outputs=[txt_3])
-
+
with gr.Row():
im = gr.Image()
im_2 = gr.Image()
-
+
btn = gr.Button(value="Mirror Image")
btn.click(mirror, inputs=[im], outputs=[im_2])
-
+
gr.Markdown("## Text Examples")
- gr.Examples([["hi", "Adam"], ["hello", "Eve"]], [txt, txt_2], txt_3, combine, cache_examples=True)
+ gr.Examples(
+ [["hi", "Adam"], ["hello", "Eve"]],
+ [txt, txt_2],
+ txt_3,
+ combine,
+ cache_examples=True,
+ )
gr.Markdown("## Image Examples")
gr.Examples(
examples=[os.path.join(os.path.dirname(__file__), "lion.jpg")],
inputs=im,
outputs=im_2,
fn=mirror,
- cache_examples=True)
+ cache_examples=True,
+ )
if __name__ == "__main__":
demo.launch()
diff --git a/gradio/components.py b/gradio/components.py
index 9cf67d0d3d..82f0363d0d 100644
--- a/gradio/components.py
+++ b/gradio/components.py
@@ -281,6 +281,7 @@ class Textbox(
interactive: Optional[bool] = None,
visible: bool = True,
elem_id: Optional[str] = None,
+ type: str = "text",
**kwargs,
):
"""
@@ -294,9 +295,14 @@ class Textbox(
interactive: if True, will be rendered as an editable textbox; if False, editing will be disabled. If not provided, this is inferred based on whether the component is used as an input or output.
visible: If False, component will be hidden.
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
+ type: The type of textbox. One of: 'text', 'password', 'email', Default is 'text'.
"""
+ if type not in ["text", "password", "email"]:
+ raise ValueError('`type` must be one of "text", "password", or "email".')
+
+ #
self.lines = lines
- self.max_lines = max_lines
+ self.max_lines = max_lines if type == "text" else 1
self.placeholder = placeholder
self.interpret_by_tokens = True
IOComponent.__init__(
@@ -311,6 +317,7 @@ class Textbox(
)
self.cleared_value = ""
self.test_input = value
+ self.type = type
def get_config(self):
return {
@@ -318,6 +325,7 @@ class Textbox(
"max_lines": self.max_lines,
"placeholder": self.placeholder,
"value": self.value,
+ "type": self.type,
**IOComponent.get_config(self),
}
@@ -331,6 +339,7 @@ class Textbox(
show_label: Optional[bool] = None,
visible: Optional[bool] = None,
interactive: Optional[bool] = None,
+ type: Optional[str] = None,
):
updated_config = {
"lines": lines,
@@ -340,6 +349,7 @@ class Textbox(
"show_label": show_label,
"visible": visible,
"value": value,
+ "type": type,
"__type__": "update",
}
return IOComponent.add_interactive_to_config(updated_config, interactive)
diff --git a/gradio/inputs.py b/gradio/inputs.py
index 6b1c7f9f8c..184c965593 100644
--- a/gradio/inputs.py
+++ b/gradio/inputs.py
@@ -19,7 +19,7 @@ class Textbox(components.Textbox):
placeholder: Optional[str] = None,
default: str = "",
numeric: Optional[bool] = False,
- type: Optional[str] = "str",
+ type: Optional[str] = "text",
label: Optional[str] = None,
optional: bool = False,
):
diff --git a/gradio/test_data/blocks_configs.py b/gradio/test_data/blocks_configs.py
index 6a1660e613..11dfa3a8da 100644
--- a/gradio/test_data/blocks_configs.py
+++ b/gradio/test_data/blocks_configs.py
@@ -126,6 +126,7 @@ XRAY_CONFIG = {
"lines": 1,
"max_lines": 20,
"value": "",
+ "type": "text",
"show_label": True,
"name": "textbox",
"visible": True,
@@ -358,6 +359,7 @@ XRAY_CONFIG_DIFF_IDS = {
"lines": 1,
"max_lines": 20,
"value": "",
+ "type": "text",
"show_label": True,
"name": "textbox",
"visible": True,
@@ -593,6 +595,7 @@ XRAY_CONFIG_WITH_MISTAKE = {
"lines": 1,
"value": "",
"name": "textbox",
+ "type": "text",
"style": {},
},
},
diff --git a/test/test_blocks.py b/test/test_blocks.py
index 03ae152b45..80ff3b004f 100644
--- a/test/test_blocks.py
+++ b/test/test_blocks.py
@@ -714,6 +714,8 @@ class TestSpecificUpdate:
"placeholder": None,
"label": None,
"show_label": None,
+ "type": None,
+ "type": None,
"visible": None,
"value": gr.components._Keywords.NO_VALUE,
"__type__": "update",
@@ -729,6 +731,7 @@ class TestSpecificUpdate:
"placeholder": None,
"label": None,
"show_label": None,
+ "type": None,
"visible": None,
"value": gr.components._Keywords.NO_VALUE,
"__type__": "update",
diff --git a/test/test_components.py b/test/test_components.py
index 3595e8aecf..d2f0f0bba5 100644
--- a/test/test_components.py
+++ b/test/test_components.py
@@ -1,5 +1,5 @@
"""
-Tests for all of the componets defined in components.py. Tests are divided into two types:
+Tests for all of the components defined in components.py. Tests are divided into two types:
1. test_component_functions() are unit tests that check essential functions of a component, the functions that are checked are documented in the docstring.
2. test_in_interface() are functional tests that check a component's functionalities inside an Interface. Please do not use Interface.launch() in this file, as it slow downs the tests.
"""
@@ -59,9 +59,6 @@ class TestTextbox:
assert text_input.postprocess(2.14) == "2.14"
assert text_input.serialize("Hello World!", True) == "Hello World!"
- with pytest.warns(Warning):
- _ = gr.Textbox(type="number")
-
assert text_input.tokenize("Hello World! Gradio speaking.") == (
["Hello", "World!", "Gradio", "speaking."],
[
@@ -90,6 +87,7 @@ class TestTextbox:
"value": "",
"name": "textbox",
"show_label": True,
+ "type": "text",
"label": None,
"style": {},
"elem_id": None,
@@ -166,6 +164,18 @@ class TestTextbox:
assert component.get_config().get("value") == "abc"
assert component.get_config().get("lines") == 4
+ def test_faulty_type(self):
+ with pytest.raises(
+ ValueError, match='`type` must be one of "text", "password", or "email".'
+ ):
+ gr.Textbox(type="boo")
+
+ def test_max_lines(self):
+ assert gr.Textbox(type="password").get_config().get("max_lines") == 1
+ assert gr.Textbox(type="email").get_config().get("max_lines") == 1
+ assert gr.Textbox(type="text").get_config().get("max_lines") == 20
+ assert gr.Textbox().get_config().get("max_lines") == 20
+
class TestNumber:
def test_component_functions(self):
diff --git a/ui/packages/app/src/components/Textbox/Textbox.svelte b/ui/packages/app/src/components/Textbox/Textbox.svelte
index fac04a7d7a..33b535a610 100644
--- a/ui/packages/app/src/components/Textbox/Textbox.svelte
+++ b/ui/packages/app/src/components/Textbox/Textbox.svelte
@@ -15,6 +15,7 @@
export let placeholder: string = "";
export let show_label: boolean;
export let max_lines: number | false;
+ export let type: "text" | "password" | "email" = "text";
export let style: Styles = {};
@@ -35,6 +36,7 @@
{label}
{show_label}
{lines}
+ {type}
max_lines={!max_lines && mode === "static" ? lines + 1 : max_lines}
{placeholder}
on:change
diff --git a/ui/packages/form/src/Textbox.svelte b/ui/packages/form/src/Textbox.svelte
index 465e966bd0..43a56c1881 100644
--- a/ui/packages/form/src/Textbox.svelte
+++ b/ui/packages/form/src/Textbox.svelte
@@ -9,6 +9,7 @@
export let disabled = false;
export let show_label: boolean = true;
export let max_lines: number | false;
+ export let type: "text" | "password" | "email" = "text";
let el: HTMLTextAreaElement | HTMLInputElement;
@@ -93,17 +94,45 @@
{label}
{#if lines === 1 && max_lines === 1}
-
+ {#if type === "text"}
+
+ {:else if type === "password"}
+
+ {:else if type === "email"}
+
+ {/if}
{:else}