From be4210918872876c13e83d47809cd500d7713dfe Mon Sep 17 00:00:00 2001 From: VijithaEkanayake Date: Fri, 18 Dec 2020 19:30:55 +0530 Subject: [PATCH] Introduce submit attribute to textbox The implementation conditionally calls the keyup handler based on the values of the submit attribute. Fixes: https://github.com/h2oai/wave/issues/372 --- py/examples/textbox.py | 10 +++------- py/h2o_wave/types.py | 7 +++++++ py/h2o_wave/ui.py | 3 +++ ui/src/textbox.test.tsx | 22 ++++++++++++++++------ ui/src/textbox.tsx | 6 ++++-- 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/py/examples/textbox.py b/py/examples/textbox.py index 2872b7cd9ea..22b4de7af96 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,11 +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.button(name='show_form', label='Back', primary=True), - ] - elif q.args.enter_key_handler: - q.page['example'].items = [ - ui.text(f'textbox_enter_key_handler={q.args.enter_key_handler}'), + ui.text(f'textbox_enter={q.args.textbox_enter}'), ui.button(name='show_form', label='Back', primary=True), ] else: @@ -41,7 +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='enter_key_handler', label='Submits the textbox value on Enter key', icon='Search'), + 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 9af0aa771cd..26fc740edc5 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 098ec0789b5..2d3b699302e 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 71eb12ab687..9e854928463 100644 --- a/ui/src/textbox.test.tsx +++ b/ui/src/textbox.test.tsx @@ -102,8 +102,8 @@ describe('Textbox.tsx', () => { expect(syncMock).not.toBeCalled() }) - it('Calls sync on enter pressed', () => { - const { getByTestId } = render() + it('Calls sync on enter pressed - submit specified', () => { + const { getByTestId } = render() const syncMock = jest.fn() T.qd.sync = syncMock @@ -112,8 +112,8 @@ describe('Textbox.tsx', () => { expect(syncMock).toBeCalled() }) - it('Does not call sync when key pressed is not enter', () => { - const { getByTestId } = render() + it('Does not call sync when key pressed is not enter - submit specified', () => { + const { getByTestId } = render() const syncMock = jest.fn() T.qd.sync = syncMock @@ -122,8 +122,18 @@ describe('Textbox.tsx', () => { expect(syncMock).not.toBeCalled() }) - it('Does not call sync on enter - multiline is true', () => { - const { getByTestId } = render() + 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 diff --git a/ui/src/textbox.tsx b/ui/src/textbox.tsx index 7d9c5644c6a..fa0e796df53 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 @@ -95,7 +97,7 @@ export const disabled={m.disabled} readOnly={m.readonly} onChange={m.trigger ? debounce(DEBOUNCE_TIMEOUT, onChange) : onChange} - onKeyUp={onKeyUp} + onKeyUp={m.submit ? onKeyUp: undefined} /> ) : ( @@ -116,7 +118,7 @@ export const multiline={m.multiline} type={m.password ? 'password' : undefined} onChange={m.trigger ? debounce(DEBOUNCE_TIMEOUT, onChange) : onChange} - onKeyUp={onKeyUp} + onKeyUp={m.submit ? onKeyUp: undefined} /> )