Skip to content

Commit 85a45bd

Browse files
committed
Fix #2612: unexpected behaviour of the cursor
When inserting a non valid character in the middle of a pattern, the cursor instantly jumps to the end of the word, instead of staying in that position. Added also some case tests.
1 parent c847882 commit 85a45bd

File tree

2 files changed

+85
-9
lines changed

2 files changed

+85
-9
lines changed

components/dash-core-components/src/components/Input.react.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -184,16 +184,23 @@ export default class Input extends PureComponent {
184184

185185
onChange() {
186186
const {debounce} = this.props;
187-
if (debounce) {
188-
if (Number.isFinite(debounce)) {
189-
this.debounceEvent(debounce);
190-
}
191-
if (this.props.type !== 'number') {
192-
this.setState({value: this.input.current.value});
187+
const input = this.input.current;
188+
const cursorPosition = input.selectionStart;
189+
const currentValue = input.value;
190+
this.setState({value: currentValue}, () => {
191+
if (debounce) {
192+
if (Number.isFinite(debounce)) {
193+
this.debounceEvent(debounce);
194+
}
195+
if (this.props.type !== 'number') {
196+
setTimeout(() => {
197+
input.setSelectionRange(cursorPosition, cursorPosition);
198+
}, 0);
199+
}
200+
} else {
201+
this.onEvent();
193202
}
194-
} else {
195-
this.onEvent();
196-
}
203+
});
197204
}
198205
}
199206

components/dash-core-components/tests/integration/input/test_input_basics.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,72 @@ def test_inbs003_styles_are_scoped(dash_dcc):
103103
dash_outline_css = dash_input.value_of_css_property("outline")
104104

105105
assert external_outline_css != dash_outline_css
106+
107+
@pytest.mark.parametrize(
108+
"initial_text, invalid_char, cursor_position_before, expected_text, expected_cursor_position",
109+
[
110+
("abcdddef", "/", 2, "ab/cdddef", 3),
111+
("abcdef", "$", 2, "ab$cdef", 3),
112+
("abcdef", "$", 3, "abc$def", 4),
113+
("abcdef", "A", 4, "abcdAef", 5), # valid character
114+
],
115+
)
116+
def test_inbs004_cursor_position_on_invalid_input(
117+
dash_dcc,
118+
initial_text,
119+
invalid_char,
120+
cursor_position_before,
121+
expected_text,
122+
expected_cursor_position,
123+
):
124+
app = Dash(__name__)
125+
126+
app.layout = html.Div(
127+
[
128+
dcc.Input(
129+
id="test-input",
130+
type="text",
131+
placeholder="File name",
132+
className="create_file_input",
133+
pattern="[a-zA-Z_][a-zA-Z0-9_]*",
134+
),
135+
html.Div(id="output"),
136+
]
137+
)
138+
139+
dash_dcc.start_server(app)
140+
input_elem = dash_dcc.find_element("#test-input")
141+
142+
input_elem.send_keys(initial_text)
143+
assert (
144+
input_elem.get_attribute("value") == initial_text
145+
), "Initial text should match"
146+
147+
dash_dcc.driver.execute_script(
148+
f"""
149+
var elem = arguments[0];
150+
elem.setSelectionRange({cursor_position_before}, {cursor_position_before});
151+
elem.focus();
152+
""",
153+
input_elem,
154+
)
155+
156+
input_elem.send_keys(invalid_char)
157+
158+
assert (
159+
input_elem.get_attribute("value") == expected_text
160+
), f"Input should be {expected_text}"
161+
162+
cursor_position = dash_dcc.driver.execute_script(
163+
"""
164+
var elem = arguments[0];
165+
return elem.selectionStart;
166+
""",
167+
input_elem,
168+
)
169+
170+
assert (
171+
cursor_position == expected_cursor_position
172+
), f"Cursor should be at position {expected_cursor_position}"
173+
174+
assert dash_dcc.get_logs() == []

0 commit comments

Comments
 (0)