Skip to content

feat(js): linked brushing via SelectionBus + Vega-Lite bridge demo#859

Open
paddymul wants to merge 1 commit into
mainfrom
feat/linked-brushing-selection-bus
Open

feat(js): linked brushing via SelectionBus + Vega-Lite bridge demo#859
paddymul wants to merge 1 commit into
mainfrom
feat/linked-brushing-selection-bus

Conversation

@paddymul
Copy link
Copy Markdown
Collaborator

Summary

  • New SelectionBus (packages/buckaroo-js-core/src/selection/SelectionBus.ts) — a frontend-only pub/sub for linked brushing. Same-page subscribers via EventTarget, cross-iframe / cross-tab via BroadcastChannel, dedupe by source so publishers don't receive their own echo.
  • New optional onGridApiReady?: (api: GridApi) => void prop on DFViewer (threaded through DFViewerInfinite / DFViewerInfiniteInner). Consumers use the AG-Grid API directly to drive applyTransaction + refreshCells({ force: true }), so a remote selection updates highlights without remounting the grid (scroll position preserved).
  • Registers the AG-Grid v33 modules required by those APIs: EventApiModule, RowApiModule, RenderApiModule, ClientSideRowModelApiModule.
  • Two Storybook demos:
    • Buckaroo / LinkedBrushing — two DFViewers on the same channel; row click in either grid highlights the matching id in the other. Buttons simulate an external publisher.
    • Buckaroo / LinkedBrushingVega — a Vega-Lite scatter brushed against a DFViewer, communicating through the same bus.
  • vega-embed is a devDependency only and is loaded via dynamic import() inside the story, so it never ends up in the published buckaroo-js-core bundle.

Test plan

  • pnpm test — 280 passed including new SelectionBus.test.ts (pub/sub roundtrip, source dedupe, channel filter, singleton).
  • pnpm exec tsc --noEmit clean.
  • Storybook smoke: pnpm storybook, open Buckaroo / LinkedBrushing — clicking rows in Grid A highlights Grid B (and vice versa); preset buttons broadcast on the bus.
  • Storybook smoke: open Buckaroo / LinkedBrushingVega — brush a region on the scatter; matching rows highlight in the DFViewer.
  • Verify in a real Jupyter notebook that two widget instances cross-brush via BroadcastChannel (not exercised in CI).

Notes

  • This PR introduces the bus + onGridApiReady plumbing, not a dedicated selection_channel / selection_key prop pair on DFViewer. That higher-level API is the natural follow-up — the bus and the api-ready callback are the load-bearing primitives it would sit on.
  • Filter mode (vs. highlight) intentionally not implemented: changing the underlying df would invalidate the analysis pipeline's summary stats. Highlight-only is the safe v1.

🤖 Generated with Claude Code

Introduces a frontend-only selection bus for linked brushing between
DFViewer instances and external publishers (e.g. Vega-Lite brushes).
Subscribers receive same-page events via EventTarget; BroadcastChannel
fan-out covers cross-iframe / cross-tab cases. Messages dedupe by
source so a publisher does not receive its own echo.

DFViewer gains an optional onGridApiReady callback so consumers can
drive `applyTransaction` + `refreshCells({force: true})` directly,
avoiding the remount-via-key workaround. Registers the AG-Grid v33
modules that back those APIs (Event/Row/Render/ClientSideRowModelApi).

Two Storybook stories demo the bus: LinkedBrushing (two DFViewers +
button publisher) and LinkedBrushingVega (a Vega-Lite scatter <->
DFViewer). vega-embed is a devDependency only and is loaded via
dynamic import(), so it never ships in the published bundle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c0026105d9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const updated = (view.data("source_0") as Row[]).map(
(d) => ({
...d,
__selected: ids.has(d.id) ? 1 : null,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Drive Vega highlight from a field used by the spec

This bus-to-Vega handler mutates __selected, but the Vega-Lite spec in this story never encodes __selected in any mark/encoding, so incoming selections from the grid do not produce a visible chart highlight despite the comment and story text claiming they do. In practice, clicking a DFViewer row publishes on the bus, the dataset is replaced, and the chart appears unchanged unless the local brush state changes. Either bind __selected into a visual channel/layer or update the brush signal directly so remote selection is actually rendered.

Useful? React with 👍 / 👎.

@github-actions
Copy link
Copy Markdown
Contributor

📦 TestPyPI package published

pip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.14.8.dev26464044337

or with uv:

uv pip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.14.8.dev26464044337

MCP server for Claude Code

claude mcp add buckaroo-table -- uvx --from "buckaroo[mcp]==0.14.8.dev26464044337" --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo-table

📖 Docs preview

🎨 Storybook preview

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant