diff --git a/py/examples/textbox.py b/py/examples/textbox.py
index 39c29bdee3..3a06c41abb 100644
--- a/py/examples/textbox.py
+++ b/py/examples/textbox.py
@@ -7,7 +7,7 @@
@app('/demo')
async def serve(q: Q):
- if q.args.show_inputs:
+ if q.args.textbox is not None:
q.page['example'].items = [
ui.text(f'textbox={q.args.textbox}'),
ui.text(f'textbox_disabled={q.args.textbox_disabled}'),
@@ -22,6 +22,7 @@ async def serve(q: Q):
ui.text(f'textbox_disabled_placeholder={q.args.textbox_disabled_placeholder}'),
ui.text(f'textbox_multiline={q.args.textbox_multiline}'),
ui.text(f'textbox_spellcheck_disabled={q.args.textbox_spellcheck_disabled}'),
+ ui.text(f'textbox_enter={q.args.textbox_enter}'),
ui.button(name='show_form', label='Back', primary=True),
]
else:
@@ -40,6 +41,7 @@ async def serve(q: Q):
placeholder='I am disabled'),
ui.textbox(name='textbox_multiline', label='Multiline textarea', multiline=True),
ui.textbox(name='textbox_spellcheck_disabled', label='Spellcheck disabled', spellcheck=False),
+ ui.textbox(name='textbox_enter', label='Submit the form when pressing the Enter key', trigger_on_enter=True),
ui.button(name='show_inputs', label='Submit', primary=True),
])
await q.page.save()
diff --git a/py/h2o_wave/types.py b/py/h2o_wave/types.py
index ff8c0cc952..cd96007263 100644
--- a/py/h2o_wave/types.py
+++ b/py/h2o_wave/types.py
@@ -990,6 +990,7 @@ def __init__(
visible: Optional[bool] = None,
tooltip: Optional[str] = None,
spellcheck: Optional[bool] = None,
+ trigger_on_enter: Optional[bool] = None,
):
_guard_scalar('Textbox.name', name, (str,), True, False, False)
_guard_scalar('Textbox.label', label, (str,), False, True, False)
@@ -1011,6 +1012,7 @@ def __init__(
_guard_scalar('Textbox.visible', visible, (bool,), False, True, False)
_guard_scalar('Textbox.tooltip', tooltip, (str,), False, True, False)
_guard_scalar('Textbox.spellcheck', spellcheck, (bool,), False, True, False)
+ _guard_scalar('Textbox.trigger_on_enter', trigger_on_enter, (bool,), False, True, False)
self.name = name
"""An identifying name for this component."""
self.label = label
@@ -1051,6 +1053,8 @@ def __init__(
"""An optional tooltip message displayed when a user clicks the help icon to the right of the component."""
self.spellcheck = spellcheck
"""True if the text may be checked for spelling errors. Defaults to True."""
+ self.trigger_on_enter = trigger_on_enter
+ """True to make the form submit when the user presses the Enter key. Defaults to False."""
def dump(self) -> Dict:
"""Returns the contents of this object as a dict."""
@@ -1074,6 +1078,7 @@ def dump(self) -> Dict:
_guard_scalar('Textbox.visible', self.visible, (bool,), False, True, False)
_guard_scalar('Textbox.tooltip', self.tooltip, (str,), False, True, False)
_guard_scalar('Textbox.spellcheck', self.spellcheck, (bool,), False, True, False)
+ _guard_scalar('Textbox.trigger_on_enter', self.trigger_on_enter, (bool,), False, True, False)
return _dump(
name=self.name,
label=self.label,
@@ -1095,6 +1100,7 @@ def dump(self) -> Dict:
visible=self.visible,
tooltip=self.tooltip,
spellcheck=self.spellcheck,
+ trigger_on_enter=self.trigger_on_enter,
)
@staticmethod
@@ -1140,6 +1146,8 @@ def load(__d: Dict) -> 'Textbox':
_guard_scalar('Textbox.tooltip', __d_tooltip, (str,), False, True, False)
__d_spellcheck: Any = __d.get('spellcheck')
_guard_scalar('Textbox.spellcheck', __d_spellcheck, (bool,), False, True, False)
+ __d_trigger_on_enter: Any = __d.get('trigger_on_enter')
+ _guard_scalar('Textbox.trigger_on_enter', __d_trigger_on_enter, (bool,), False, True, False)
name: str = __d_name
label: Optional[str] = __d_label
placeholder: Optional[str] = __d_placeholder
@@ -1160,6 +1168,7 @@ def load(__d: Dict) -> 'Textbox':
visible: Optional[bool] = __d_visible
tooltip: Optional[str] = __d_tooltip
spellcheck: Optional[bool] = __d_spellcheck
+ trigger_on_enter: Optional[bool] = __d_trigger_on_enter
return Textbox(
name,
label,
@@ -1181,6 +1190,7 @@ def load(__d: Dict) -> 'Textbox':
visible,
tooltip,
spellcheck,
+ trigger_on_enter,
)
diff --git a/py/h2o_wave/ui.py b/py/h2o_wave/ui.py
index 1d52970192..a095dd5cc3 100644
--- a/py/h2o_wave/ui.py
+++ b/py/h2o_wave/ui.py
@@ -400,6 +400,7 @@ def textbox(
visible: Optional[bool] = None,
tooltip: Optional[str] = None,
spellcheck: Optional[bool] = None,
+ trigger_on_enter: Optional[bool] = None,
) -> Component:
"""Create a text box.
@@ -428,6 +429,7 @@ def textbox(
visible: True if the component should be visible. Defaults to True.
tooltip: An optional tooltip message displayed when a user clicks the help icon to the right of the component.
spellcheck: True if the text may be checked for spelling errors. Defaults to True.
+ trigger_on_enter: True to make the form submit when the user presses the Enter key. Defaults to False.
Returns:
A `h2o_wave.types.Textbox` instance.
"""
@@ -452,6 +454,7 @@ def textbox(
visible,
tooltip,
spellcheck,
+ trigger_on_enter,
))
diff --git a/r/R/ui.R b/r/R/ui.R
index c608f42f5d..92abbc5c12 100644
--- a/r/R/ui.R
+++ b/r/R/ui.R
@@ -484,6 +484,7 @@ ui_message_bar <- function(
#' @param visible True if the component should be visible. Defaults to True.
#' @param tooltip An optional tooltip message displayed when a user clicks the help icon to the right of the component.
#' @param spellcheck True if the text may be checked for spelling errors. Defaults to True.
+#' @param trigger_on_enter True to make the form submit when the user presses the Enter key. Defaults to False.
#' @return A Textbox instance.
#' @export
ui_textbox <- function(
@@ -506,7 +507,8 @@ ui_textbox <- function(
width = NULL,
visible = NULL,
tooltip = NULL,
- spellcheck = NULL) {
+ spellcheck = NULL,
+ trigger_on_enter = NULL) {
.guard_scalar("name", "character", name)
.guard_scalar("label", "character", label)
.guard_scalar("placeholder", "character", placeholder)
@@ -527,6 +529,7 @@ ui_textbox <- function(
.guard_scalar("visible", "logical", visible)
.guard_scalar("tooltip", "character", tooltip)
.guard_scalar("spellcheck", "logical", spellcheck)
+ .guard_scalar("trigger_on_enter", "logical", trigger_on_enter)
.o <- list(textbox=list(
name=name,
label=label,
@@ -547,7 +550,8 @@ ui_textbox <- function(
width=width,
visible=visible,
tooltip=tooltip,
- spellcheck=spellcheck))
+ spellcheck=spellcheck,
+ trigger_on_enter=trigger_on_enter))
class(.o) <- append(class(.o), c(.wave_obj, "WaveComponent"))
return(.o)
}
diff --git a/tools/intellij-plugin/src/main/resources/templates/wave-components.xml b/tools/intellij-plugin/src/main/resources/templates/wave-components.xml
index 92ad7def36..d1f5651ee1 100644
--- a/tools/intellij-plugin/src/main/resources/templates/wave-components.xml
+++ b/tools/intellij-plugin/src/main/resources/templates/wave-components.xml
@@ -2499,7 +2499,7 @@
-
+
@@ -2520,6 +2520,7 @@
+
diff --git a/tools/vscode-extension/component-snippets.json b/tools/vscode-extension/component-snippets.json
index 59763b50f9..24e3c6d728 100644
--- a/tools/vscode-extension/component-snippets.json
+++ b/tools/vscode-extension/component-snippets.json
@@ -1822,7 +1822,7 @@
"Wave Full Textbox": {
"prefix": "w_full_textbox",
"body": [
- "ui.textbox(name='$1', label='$2', placeholder='$3', value='$4', mask='$5', icon='$6', prefix='$7', suffix='$8', error='$9', required=${10:False}, disabled=${11:False}, readonly=${12:False}, multiline=${13:False}, password=${14:False}, trigger=${15:False}, height='$16', width='${17:100%}', visible=${18:True}, tooltip='$19', spellcheck=${20:True}),$0"
+ "ui.textbox(name='$1', label='$2', placeholder='$3', value='$4', mask='$5', icon='$6', prefix='$7', suffix='$8', error='$9', required=${10:False}, disabled=${11:False}, readonly=${12:False}, multiline=${13:False}, password=${14:False}, trigger=${15:False}, height='$16', width='${17:100%}', visible=${18:True}, tooltip='$19', spellcheck=${20:True}, trigger_on_enter=${21:False}),$0"
],
"description": "Create a full Wave Textbox."
},
diff --git a/ui/src/textbox.test.tsx b/ui/src/textbox.test.tsx
index 26a3469c75..d55639b7c1 100644
--- a/ui/src/textbox.test.tsx
+++ b/ui/src/textbox.test.tsx
@@ -157,4 +157,16 @@ describe('Textbox.tsx', () => {
rerender()
expect(getByTestId(name)).toHaveValue('(456)')
})
+
+ it('Doesn\'t submit when the Enter key is pressed by default', () => {
+ const { getByTestId } = render()
+ userEvent.type(getByTestId(name), '123{Enter}')
+ expect(pushMock).not.toBeCalled()
+ })
+
+ it('Submits when the Enter key is pressed using the trigger_on_enter option', () => {
+ const { getByTestId } = render()
+ userEvent.type(getByTestId(name), '123{Enter}')
+ expect(pushMock).toBeCalled()
+ })
})
\ No newline at end of file
diff --git a/ui/src/textbox.tsx b/ui/src/textbox.tsx
index b6c159f662..8125039c60 100644
--- a/ui/src/textbox.tsx
+++ b/ui/src/textbox.tsx
@@ -65,6 +65,8 @@ export interface Textbox {
tooltip?: S
/** True if the text may be checked for spelling errors. Defaults to True. */
spellcheck?: B
+ /** True to make the form submit when the user presses the Enter key. Defaults to False. */
+ trigger_on_enter?: B
}
const DEBOUNCE_TIMEOUT = 500
@@ -81,6 +83,11 @@ export const
setValue(v)
m.value = v
},
+ onKeyDown = ({ key } : { key: string }) => {
+ if (m.trigger_on_enter && key === 'Enter') {
+ wave.push()
+ }
+ },
textFieldProps: Fluent.ITextFieldProps & { 'data-test': S } = {
'data-test': m.name,
label: m.label,
@@ -90,6 +97,7 @@ export const
disabled: m.disabled,
readOnly: m.readonly,
onChange,
+ onKeyDown,
iconProps: m.icon ? { iconName: m.icon } : undefined,
placeholder: m.placeholder,
prefix: m.prefix,
diff --git a/website/widgets/form/textbox.md b/website/widgets/form/textbox.md
index 68cca3a668..965012a27d 100644
--- a/website/widgets/form/textbox.md
+++ b/website/widgets/form/textbox.md
@@ -11,7 +11,7 @@ know what they are expected to type. Another common UX error is misusing `placeh
a label which is not correct as the placeholder value should be an example value, e.g. for name field
the placeholder could be `John Doe`.
-Note that if `trigger` is specified, the inputs are submitted after `500ms` of no typing activity. This threshold is currently not configurable.
+Note that if `trigger` is specified, the inputs are submitted after `500ms` of no typing activity. This threshold is currently not configurable. You can also use the `trigger_on_enter` option so that users can submit by pressing the Enter key.
The `name` attribute indicates how to reference this component in the query arguments: `q.args.`.