feat(forms): add a debounce for Input and Textarea fields

fix #2415
This commit is contained in:
Yohan Boniface 2025-01-24 12:01:25 +01:00
parent ea2bdba270
commit 49ea7ed4a5
7 changed files with 31 additions and 6 deletions

View file

@ -132,7 +132,10 @@ Fields.Textarea = class extends BaseElement {
super.build() super.build()
this.textarea = this.elements.textarea this.textarea = this.elements.textarea
this.fetch() this.fetch()
this.textarea.addEventListener('input', () => this.sync()) this.textarea.addEventListener(
'input',
Utils.debounce(() => this.sync(), 300)
)
this.textarea.addEventListener('keypress', (event) => this.onKeyPress(event)) this.textarea.addEventListener('keypress', (event) => this.onKeyPress(event))
} }
@ -179,7 +182,7 @@ Fields.Input = class extends BaseElement {
this.input.step = this.properties.step this.input.step = this.properties.step
} }
this.fetch() this.fetch()
this.input.addEventListener(this.getSyncEvent(), () => this.sync()) this.listenForSync()
this.input.addEventListener('keydown', (event) => this.onKeyDown(event)) this.input.addEventListener('keydown', (event) => this.onKeyDown(event))
} }
@ -189,8 +192,11 @@ Fields.Input = class extends BaseElement {
this.input.value = value this.input.value = value
} }
getSyncEvent() { listenForSync() {
return 'input' this.input.addEventListener(
'input',
Utils.debounce(() => this.sync(), 300)
)
} }
type() { type() {
@ -212,8 +218,8 @@ Fields.Input = class extends BaseElement {
} }
Fields.BlurInput = class extends Fields.Input { Fields.BlurInput = class extends Fields.Input {
getSyncEvent() { listenForSync() {
return 'blur' this.input.addEventListener('blur', () => this.sync())
} }
getTemplate() { getTemplate() {

View file

@ -471,6 +471,19 @@ export function isWritable(element) {
return false return false
} }
// From https://www.joshwcomeau.com/snippets/javascript/debounce/
export const debounce = (callback, wait) => {
let timeoutId = null
return (...args) => {
window.clearTimeout(timeoutId)
timeoutId = window.setTimeout(() => {
callback.apply(null, args)
}, wait)
}
}
export const COLORS = [ export const COLORS = [
'Black', 'Black',
'Navy', 'Navy',

View file

@ -24,6 +24,7 @@ def test_layers_list_is_updated(live_server, tilelayer, page):
page.get_by_role("button", name="Add a layer").click() page.get_by_role("button", name="Add a layer").click()
page.locator('input[name="name"]').click() page.locator('input[name="name"]').click()
page.locator('input[name="name"]').fill("foobar") page.locator('input[name="name"]').fill("foobar")
page.wait_for_timeout(300) # Time for the input debounce.
page.get_by_role("link", name=f"Import data ({modifier}+I)").click() page.get_by_role("link", name=f"Import data ({modifier}+I)").click()
# Should still work # Should still work
page.locator("[name=layer-id]").select_option(label="Import in a new layer") page.locator("[name=layer-id]").select_option(label="Import in a new layer")

View file

@ -285,6 +285,7 @@ def test_should_display_alert_on_conflict(context, live_server, datalayer, openm
# Change name on page one and save # Change name on page one and save
page_one.locator(".leaflet-marker-icon").click(modifiers=["Shift"]) page_one.locator(".leaflet-marker-icon").click(modifiers=["Shift"])
page_one.locator('input[name="name"]').fill("name from page one") page_one.locator('input[name="name"]').fill("name from page one")
page_one.wait_for_timeout(300) # Time for the input debounce.
with page_one.expect_response(re.compile(r".*/datalayer/update/.*")): with page_one.expect_response(re.compile(r".*/datalayer/update/.*")):
page_one.get_by_role("button", name="Save").click() page_one.get_by_role("button", name="Save").click()

View file

@ -24,6 +24,7 @@ def test_reseting_map_would_remove_from_save_queue(
page.get_by_role("button", name="Edit", exact=True).click() page.get_by_role("button", name="Edit", exact=True).click()
page.locator('input[name="name"]').click() page.locator('input[name="name"]').click()
page.locator('input[name="name"]').fill("new datalayer name") page.locator('input[name="name"]').fill("new datalayer name")
page.wait_for_timeout(300) # Time of the Input debounce
with page.expect_response(re.compile(".*/datalayer/update/.*")): with page.expect_response(re.compile(".*/datalayer/update/.*")):
page.get_by_role("button", name="Save").click() page.get_by_role("button", name="Save").click()
assert len(requests) == 1 assert len(requests) == 1

View file

@ -74,6 +74,7 @@ def test_table_editor(live_server, openmap, datalayer, page):
page.locator("dialog").get_by_role("button", name="OK").click() page.locator("dialog").get_by_role("button", name="OK").click()
page.locator("td").nth(2).dblclick() page.locator("td").nth(2).dblclick()
page.locator('input[name="newprop"]').fill("newvalue") page.locator('input[name="newprop"]').fill("newvalue")
page.wait_for_timeout(300) # Time for the input debounce.
page.keyboard.press("Enter") page.keyboard.press("Enter")
page.locator("thead button[data-property=name]").click() page.locator("thead button[data-property=name]").click()
page.get_by_role("button", name="Delete this column").click() page.get_by_role("button", name="Delete this column").click()

View file

@ -44,6 +44,7 @@ def test_websocket_connection_can_sync_markers(
expect(peerB.get_by_role("button", name="Cancel edits")).to_be_hidden() expect(peerB.get_by_role("button", name="Cancel edits")).to_be_hidden()
peerA.locator("body").type("Synced name") peerA.locator("body").type("Synced name")
peerA.locator("body").press("Escape") peerA.locator("body").press("Escape")
peerA.wait_for_timeout(300)
peerB.locator(".leaflet-marker-icon").first.click() peerB.locator(".leaflet-marker-icon").first.click()
peerB.get_by_role("link", name="Toggle edit mode (⇧+Click)").click() peerB.get_by_role("link", name="Toggle edit mode (⇧+Click)").click()
@ -310,6 +311,7 @@ def test_websocket_connection_can_sync_late_joining_peer(
a_map_el.click(position={"x": 220, "y": 220}) a_map_el.click(position={"x": 220, "y": 220})
peerA.locator("body").type("First marker") peerA.locator("body").type("First marker")
peerA.locator("body").press("Escape") peerA.locator("body").press("Escape")
peerA.wait_for_timeout(300)
# Add a polygon from peer A # Add a polygon from peer A
create_polygon = peerA.locator(".leaflet-control-toolbar ").get_by_title( create_polygon = peerA.locator(".leaflet-control-toolbar ").get_by_title(