From 342126ab836fad4e82c8b652849ebe3f8be95bf5 Mon Sep 17 00:00:00 2001 From: Adrian Borrmann Date: Fri, 30 Jun 2023 13:45:27 -0600 Subject: [PATCH 01/26] Add ability to debounce an input field by amount of time --- .../src/components/Input.react.js | 48 ++++++++++++-- .../tests/integration/input/conftest.py | 65 +++++++++++++++++++ .../tests/integration/input/test_debounce.py | 57 ++++++++++++++++ requires-all.txt | 2 +- 4 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 components/dash-core-components/tests/integration/input/test_debounce.py diff --git a/components/dash-core-components/src/components/Input.react.js b/components/dash-core-components/src/components/Input.react.js index 18bf374a99..71435d4642 100644 --- a/components/dash-core-components/src/components/Input.react.js +++ b/components/dash-core-components/src/components/Input.react.js @@ -26,6 +26,7 @@ export default class Input extends PureComponent { this.onChange = this.onChange.bind(this); this.onEvent = this.onEvent.bind(this); this.onKeyPress = this.onKeyPress.bind(this); + this.debounceEvent = this.debounceEvent.bind(this); this.setInputValue = this.setInputValue.bind(this); this.setPropValue = this.setPropValue.bind(this); } @@ -123,13 +124,30 @@ export default class Input extends PureComponent { } } + debounceEvent() { + const {value} = this.input.current; + let {debounce} = this.props; + debounce = Number.isFinite(debounce) ? debounce * 1000 : 500; + + window.clearTimeout(this.state?.pendingEvent); + const pendingEvent = window.setTimeout(() => { + this.onEvent(); + }, debounce); + + this.setState({ + ...this.state, + value, + pendingEvent, + }); + } + onBlur() { this.props.setProps({ n_blur: this.props.n_blur + 1, n_blur_timestamp: Date.now(), }); this.input.current.checkValidity(); - return this.props.debounce && this.onEvent(); + return this.props.debounce === true && this.onEvent(); } onKeyPress(e) { @@ -140,15 +158,29 @@ export default class Input extends PureComponent { }); this.input.current.checkValidity(); } - return this.props.debounce && e.key === 'Enter' && this.onEvent(); + return this.props.debounce === true && e.key === 'Enter' && this.onEvent(); } onChange() { - if (!this.props.debounce) { + if (this.props.debounce) { + if (this.props.type !== 'number') { + // this.setState({value: this.input.current.value}); + } + if (typeof this.props.debounce === 'number') { + this.debounceEvent(); + } + } else { this.onEvent(); - } else if (this.props.type !== 'number') { - this.setState({value: this.input.current.value}); } + + + // if (!this.props.debounce) { + // this.onEvent(); + // } else if (this.props.type !== 'number') { + // this.setState({value: this.input.current.value}); + // } else if (typeof this.props.debounce === 'number') { + // this.debounceEvent(); + // } } } @@ -188,9 +220,11 @@ Input.propTypes = { /** * If true, changes to input will be sent back to the Dash server only on enter or when losing focus. - * If it's false, it will sent the value back on every change. + * If it's false, it will send the value back on every change. + * If a number, it will wait for the user to stop changing the input for that number of seconds before + * sending the value back */ - debounce: PropTypes.bool, + debounce: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]), /** * A hint to the user of what can be entered in the control . The placeholder text must not contain carriage returns or line-feeds. Note: Do not use the placeholder attribute instead of a