diff --git a/py/examples/textbox.py b/py/examples/textbox.py
index e86295d7db..22b4de7af9 100644
--- a/py/examples/textbox.py
+++ b/py/examples/textbox.py
@@ -6,7 +6,7 @@
@app('/demo')
async def serve(q: Q):
- if q.args.show_inputs:
+ if q.args.show_inputs or q.args.textbox_submit:
q.page['example'].items = [
ui.text(f'textbox={q.args.textbox}'),
ui.text(f'textbox_disabled={q.args.textbox_disabled}'),
@@ -20,6 +20,7 @@ async def serve(q: Q):
ui.text(f'textbox_placeholder={q.args.textbox_placeholder}'),
ui.text(f'textbox_disabled_placeholder={q.args.textbox_disabled_placeholder}'),
ui.text(f'textbox_multiline={q.args.textbox_multiline}'),
+ ui.text(f'textbox_enter={q.args.textbox_enter}'),
ui.button(name='show_form', label='Back', primary=True),
]
else:
@@ -36,6 +37,7 @@ async def serve(q: Q):
ui.textbox(name='textbox_placeholder', label='With placeholder', placeholder='I need some input'),
ui.textbox(name='textbox_disabled_placeholder', label='Disabled with placeholder', disabled=True,
placeholder='I am disabled'),
+ ui.textbox(name='textbox_submit', label='Submits on enter pressed', icon='Search', submit=True),
ui.textbox(name='textbox_multiline', label='Multiline textarea', multiline=True),
ui.button(name='show_inputs', label='Submit', primary=True),
])
diff --git a/py/h2o_wave/types.py b/py/h2o_wave/types.py
index 9af0aa771c..26fc740edc 100644
--- a/py/h2o_wave/types.py
+++ b/py/h2o_wave/types.py
@@ -946,6 +946,7 @@ def __init__(
height: Optional[str] = None,
visible: Optional[bool] = None,
tooltip: Optional[str] = None,
+ submit: Optional[bool] = None,
):
self.name = name
"""An identifying name for this component."""
@@ -983,6 +984,8 @@ def __init__(
"""True if the component should be visible. Defaults to true."""
self.tooltip = tooltip
"""An optional tooltip message displayed when a user clicks the help icon to the right of the component."""
+ self.submit = submit
+ """True if the form should be submitted when enter key pressed."""
def dump(self) -> Dict:
"""Returns the contents of this object as a dict."""
@@ -1007,6 +1010,7 @@ def dump(self) -> Dict:
height=self.height,
visible=self.visible,
tooltip=self.tooltip,
+ submit=self.submit,
)
@staticmethod
@@ -1032,6 +1036,7 @@ def load(__d: Dict) -> 'Textbox':
__d_height: Any = __d.get('height')
__d_visible: Any = __d.get('visible')
__d_tooltip: Any = __d.get('tooltip')
+ __d_submit: Any = __d.get('submit')
name: str = __d_name
label: Optional[str] = __d_label
placeholder: Optional[str] = __d_placeholder
@@ -1050,6 +1055,7 @@ def load(__d: Dict) -> 'Textbox':
height: Optional[str] = __d_height
visible: Optional[bool] = __d_visible
tooltip: Optional[str] = __d_tooltip
+ submit: Optional[bool] = __d_submit
return Textbox(
name,
label,
@@ -1069,6 +1075,7 @@ def load(__d: Dict) -> 'Textbox':
height,
visible,
tooltip,
+ submit,
)
diff --git a/py/h2o_wave/ui.py b/py/h2o_wave/ui.py
index 098ec0789b..2d3b699302 100644
--- a/py/h2o_wave/ui.py
+++ b/py/h2o_wave/ui.py
@@ -450,6 +450,7 @@ def textbox(
height: Optional[str] = None,
visible: Optional[bool] = None,
tooltip: Optional[str] = None,
+ submit: Optional[bool] = None,
) -> Component:
"""Create a text box.
@@ -476,6 +477,7 @@ def textbox(
height: The height of the text box, e.g. '100px'. Applicable only if `multiline` is true.
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.
+ submit: True if the form should be submitted when enter key pressed.
Returns:
A `h2o_wave.types.Textbox` instance.
"""
@@ -498,6 +500,7 @@ def textbox(
height,
visible,
tooltip,
+ submit,
))
diff --git a/ui/src/textbox.test.tsx b/ui/src/textbox.test.tsx
index b9d3b2c8e2..9e85492846 100644
--- a/ui/src/textbox.test.tsx
+++ b/ui/src/textbox.test.tsx
@@ -101,4 +101,45 @@ describe('Textbox.tsx', () => {
expect(syncMock).not.toBeCalled()
})
-})
\ No newline at end of file
+
+ it('Calls sync on enter pressed - submit specified', () => {
+ const { getByTestId } = render()
+ const syncMock = jest.fn()
+
+ T.qd.sync = syncMock
+ fireEvent.keyUp(getByTestId(name), { key: 'Enter', target: { value: 'text' } })
+
+ expect(syncMock).toBeCalled()
+ })
+
+ it('Does not call sync when key pressed is not enter - submit specified', () => {
+ const { getByTestId } = render()
+ const syncMock = jest.fn()
+
+ T.qd.sync = syncMock
+ fireEvent.keyUp(getByTestId(name), { key: 'A', target: { value: 'text' } })
+
+ expect(syncMock).not.toBeCalled()
+ })
+
+ it('Does not call sync on enter pressed - submit not specified', () => {
+ const { getByTestId } = render()
+ const syncMock = jest.fn()
+
+ T.qd.sync = syncMock
+ fireEvent.keyUp(getByTestId(name), { key: 'Enter', target: { value: 'text' } })
+
+ expect(syncMock).not.toBeCalled()
+ })
+
+ it('Does not call sync on enter - multiline and submit both are true', () => {
+ const { getByTestId } = render()
+ const syncMock = jest.fn()
+
+ T.qd.sync = syncMock
+ fireEvent.keyUp(getByTestId(name), { key: 'Enter', target: { value: 'text' } })
+
+ expect(syncMock).not.toBeCalled()
+ })
+
+})
diff --git a/ui/src/textbox.tsx b/ui/src/textbox.tsx
index dbb883b623..7d1368be8b 100644
--- a/ui/src/textbox.tsx
+++ b/ui/src/textbox.tsx
@@ -62,6 +62,8 @@ export interface Textbox {
visible?: B
/** An optional tooltip message displayed when a user clicks the help icon to the right of the component. */
tooltip?: S
+ /** True if the form should be submitted when enter key is pressed. */
+ submit?: B
}
const DEBOUNCE_TIMEOUT = 500
@@ -75,6 +77,13 @@ export const
qd.args[m.name] = v ?? (m.value || '')
if (m.trigger) qd.sync()
},
+ onKeyUp = ( {key, target}: React.KeyboardEvent, v?: S) => {
+ if (key == 'Enter' && target instanceof HTMLInputElement) {
+ v = v || target.value
+ qd.args[m.name] = v ?? (m.value || '')
+ qd.sync()
+ }
+ },
render = () => m.mask
? (
)
: (
@@ -108,8 +118,9 @@ export const
multiline={m.multiline}
type={m.password ? 'password' : undefined}
onChange={m.trigger ? debounce(DEBOUNCE_TIMEOUT, onChange) : onChange}
+ onKeyUp={m.submit ? onKeyUp : undefined}
/>
)
return { render }
- })
\ No newline at end of file
+ })