Repository: holoviz/holoviews

Issue:
Simple streams example not working
#### ALL software version info
(this library, plus any other relevant software, e.g. bokeh, python, notebook, OS, browser, etc)

Mac OS 14.3
Arc Browser Version 1.28.0, Chromium Engine Version 121.0.6167.139

holoviews     1.18.1           pyhd8ed1ab_0          3.2 MiB    conda  holoviews-1.18.1-pyhd8ed1ab_0.conda
jupyterlab    4.0.12           pyhd8ed1ab_0          5.4 MiB    conda  jupyterlab-4.0.12-pyhd8ed1ab_0.conda
numpy         1.26.3           py312h8442bc7_0       5.8 MiB    conda  numpy-1.26.3-py312h8442bc7_0.conda
pandas        2.2.0            py312h88edd18_0       13.8 MiB   conda  pandas-2.2.0-py312h88edd18_0.conda
pyviz_comms   3.0.0            pyhd8ed1ab_0          46.5 KiB   conda  pyviz_comms-3.0.0-pyhd8ed1ab_0.conda


#### Description of expected behavior and the observed behavior

The boundsx stream example from the documentation doesn't update when bounds are selected.

#### Complete, minimal, self-contained example code that reproduces the issue

```
import numpy as np
import holoviews as hv
import pandas as pd

hv.extension("bokeh")

n=200
xs = np.linspace(0, 1, n)
ys = np.cumsum(np.random.randn(n))
df = pd.DataFrame({'x': xs, 'y': ys})
curve = hv.Curve(df)

def make_from_boundsx(boundsx):
    sub = df.set_index('x').loc[boundsx[0]:boundsx[1]]
    return hv.Table(sub.describe().reset_index().values, 'stat', 'value')

dmap = hv.DynamicMap(make_from_boundsx, streams=[hv.streams.BoundsX(source=curve, boundsx=(0,0))])

curve.opts(tools=['xbox_select']) + dmap
```

#### Stack traceback and/or browser JavaScript console output

No relevant errors that I can see.

#### Screenshots or screencasts of the bug in action

<img width="730" alt="Screenshot 2024-02-05 at 16 53 55" src="https://github.com/holoviz/holoviews/assets/782987/58fe35c5-01cd-4460-a41d-f2b55bbd7a79">

After selecting a subset of the plot using the boundsx tool, the table does not update.


Discussion:

Resolving patch:
diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py
--- a/holoviews/plotting/bokeh/callbacks.py
+++ b/holoviews/plotting/bokeh/callbacks.py
@@ -833,7 +833,7 @@ class BoundsXCallback(Callback):
     models = ['plot']
     on_events = ['selectiongeometry']
 
-    skip_events = [lambda event: event.geometry['type'] != 'quad',
+    skip_events = [lambda event: event.geometry['type'] != 'rect',
                    lambda event: not event.final]
 
     def _process_msg(self, msg):
@@ -856,7 +856,7 @@ class BoundsYCallback(Callback):
     models = ['plot']
     on_events = ['selectiongeometry']
 
-    skip_events = [lambda event: event.geometry['type'] != 'quad',
+    skip_events = [lambda event: event.geometry['type'] != 'rect',
                    lambda event: not event.final]
 
     def _process_msg(self, msg):


Test patch:
diff --git a/holoviews/tests/ui/bokeh/test_callback.py b/holoviews/tests/ui/bokeh/test_callback.py
--- a/holoviews/tests/ui/bokeh/test_callback.py
+++ b/holoviews/tests/ui/bokeh/test_callback.py
@@ -16,16 +16,24 @@
 
 import holoviews as hv
 from holoviews import Curve, DynamicMap, Scatter
-from holoviews.streams import BoundsXY, Lasso, RangeXY
+from holoviews.streams import BoundsX, BoundsXY, BoundsY, Lasso, RangeXY
 
 
 @pytest.mark.usefixtures("bokeh_backend")
-def test_box_select(page, port):
+@pytest.mark.parametrize(
+    ["BoundsTool", "bound_slice", "bound_attr"],
+    [
+        (BoundsXY, slice(None), "bounds"),
+        (BoundsX, slice(0, None, 2), "boundsx"),
+        (BoundsY, slice(1, None, 2), "boundsy"),
+    ],
+)
+def test_box_select(page, port, BoundsTool, bound_slice, bound_attr):
     hv_scatter = Scatter([1, 2, 3]).opts(
         tools=['box_select'], active_tools=['box_select']
     )
 
-    bounds = BoundsXY(source=hv_scatter)
+    bounds = BoundsTool(source=hv_scatter)
 
     pn_scatter = HoloViews(hv_scatter)
 
@@ -45,7 +53,7 @@ def test_box_select(page, port):
     page.mouse.up()
 
     expected_bounds = (0.32844036697247725, 1.8285714285714285, 0.8788990825688077, 2.3183673469387758)
-    wait_until(lambda: bounds.bounds == expected_bounds, page)
+    wait_until(lambda: getattr(bounds, bound_attr) == expected_bounds[bound_slice], page)
 
 
 @pytest.mark.usefixtures("bokeh_backend")