Skip to content

Fix BarWidget Python-side sync and button query scoping - #44

Open
cpsievert wants to merge 1 commit into
koaning:mainfrom
cpsievert:bar-widget-python-sync
Open

cpsievert wants to merge 1 commit into
koaning:mainfrom
cpsievert:bar-widget-python-sync

Conversation

@cpsievert

Copy link
Copy Markdown

Fixes #42, fixes #43.

What was broken

#42 — BarWidget ignored Python and clobbered constructor data. render() initialized every collection from scratch (new Array(n_bins).fill(y_min)) and immediately called updateDataOut(), which synced that zeroed array back to Python — discarding any data passed to the constructor. And with no model.on("change:...") handlers (unlike ScatterWidget, which has four), later Python-side updates to data/n_bins/y_min/y_max had no effect until a full re-render.

#43 — document-level button queries. render() ended with document.querySelector('button.control').click(), which throws TypeError: Cannot read properties of null (reading 'click') in hosts that call render() before attaching the view's element (e.g. Shiny for Python), and clicks another BarWidget's button when more than one is on the page. The document.querySelectorAll('button.control') click handler similarly made every BarWidget's buttons switch every other BarWidget's active collection (in Jupyter too).

The fix (js/bar_widget.js, bundle rebuilt)

  • Seed from the model: a seedFromModel() helper (re)fills each collection's bins from model.get("data"), falling back to y_min for uncovered bins and clamping to [y_min, y_max] like drawn values. Called at render, so constructor data now survives — the initial updateDataOut() just normalizes/round-trips it.
  • change: handlers for data (re-seed + redraw), n_bins (re-fit bins, keeping the ones that still exist), and y_min/y_max (redraw). The change:data handler skips the echo of updateDataOut()'s own model.set() (which fires change synchronously) via a syncingDataOut flag, so there's no update loop.
  • Scoped queries: document.querySelector(All) → container.querySelector(All), so render works before document attachment and widgets can't interfere with each other.

width/height/collection_names changes still require a re-render — happy to add those here if you'd like.

Testing

Fixes koaning#42, fixes koaning#43.

BarWidget's frontend now:
- seeds its collections from the model's existing data at render time,
  instead of discarding constructor-provided data and immediately
  overwriting it with all-y_min bins (koaning#42)
- registers change: handlers for data, n_bins, y_min, and y_max, so
  Python-side updates take effect without a re-render (koaning#42). The
  change:data handler skips the echo of its own model.set() in
  updateDataOut() (which fires change synchronously) to avoid a loop
- scopes its collection-button queries to its own container instead of
  the whole document, so render no longer throws when the view isn't
  attached to the document yet, the initial .click() can't hit another
  widget's button, and multiple BarWidgets on a page no longer switch
  each other's active collection (koaning#43)

width/height/collection_names changes still require a re-render.

Tests: static regression checks on the bundle (same style as
tests/test_scatter_widget_init_order.py), plus manual verification in a
live Shiny for Python app (via shinywidgets): constructor seeding,
Python-side data=/n_bins= pushes, and two BarWidgets coexisting without
cross-contamination all work; no render-time console errors.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant