From 4b6c437aa3114ae0f677f97f57afbb007591ce12 Mon Sep 17 00:00:00 2001 From: Andrew <15331990+ahuang11@users.noreply.github.com> Date: Wed, 31 Jul 2024 00:20:24 -0700 Subject: [PATCH] Fixes popup being invisible forever after closing once (#6347) --- holoviews/plotting/bokeh/callbacks.py | 17 +++++---- holoviews/tests/ui/bokeh/test_callback.py | 46 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 0b4b95a3de..5e6011aafc 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -687,23 +687,22 @@ def _process_selection_event(self): if popup is not None: break - if callable(popup): + popup_is_callable = callable(popup) + if popup_is_callable: with set_curdoc(self.plot.document): popup = popup(**stream.contents) - # If no popup is defined, hide the panel + # If no popup is defined, hide the bokeh panel wrapper if popup is None: if self._panel.visible: self._panel.visible = False return - if event is not None: - position = self._get_position(event) - else: - position = None - popup_pane = panel(popup) - if not popup_pane.visible: + # offer the user ability to control when the popup bk panel shows up + # however, if the popup is not callable (singleton), we cannot do this + # check--else, it'll never re-appear if they set `visible=False` once + if popup_is_callable and not popup_pane.visible: return if not popup_pane.stylesheets: @@ -724,6 +723,8 @@ def _process_selection_event(self): # otherwise, UnknownReferenceError: can't resolve reference 'p...' # meaning the popup has already been removed; we need to regenerate if self._existing_popup and not self._existing_popup.visible: + self._existing_popup.visible = True + position = self._get_position(event) if event else None if position: self._panel.position = XY(**position) if self.plot.comm: # update Jupyter Notebook diff --git a/holoviews/tests/ui/bokeh/test_callback.py b/holoviews/tests/ui/bokeh/test_callback.py index 395dec132f..725df28698 100644 --- a/holoviews/tests/ui/bokeh/test_callback.py +++ b/holoviews/tests/ui/bokeh/test_callback.py @@ -315,6 +315,11 @@ def popup_form(x, y): page.click(".bk-btn.bk-btn-default") expect(locator).not_to_be_visible() + hv_plot.click() + locator = page.locator(".bk-btn.bk-btn-default") + expect(locator).to_have_count(1) + expect(locator).to_be_visible() + @skip_popup @pytest.mark.usefixtures("bokeh_backend") @@ -346,6 +351,47 @@ def popup_form(index): expect(locator).to_have_count(1) +@skip_popup +@pytest.mark.usefixtures("bokeh_backend") +def test_stream_popup_noncallable_reappear(serve_hv, points): + def popup_form(name): + text_input = pn.widgets.TextInput(name='Description') + button = pn.widgets.Button( + name='Save', + on_click=lambda _: layout.param.update(visible=False), + button_type="primary" + ) + layout = pn.Column(f'# {name}', text_input, button) + return layout + + points = points.opts(hit_dilation=5) + hv.streams.Tap(source=points, popup=popup_form('Tap')) + points.opts(tools=["tap"], active_tools=["tap"]) + + page = serve_hv(points) + hv_plot = page.locator('.bk-events') + expect(hv_plot).to_have_count(1) + + hv_plot.click() + + locator = page.locator("#tap") + expect(locator).to_have_count(1) + locator = page.locator(".bk-btn.bk-btn-primary") + expect(locator).to_have_count(1) + expect(locator).to_be_visible() + + page.click(".bk-btn.bk-btn-primary") + expect(locator).not_to_be_visible() + + hv_plot.click() + + locator = page.locator("#tap") + expect(locator).to_have_count(1) + locator = page.locator(".bk-btn.bk-btn-primary") + expect(locator).to_have_count(1) + expect(locator).to_be_visible() + + @skip_popup @pytest.mark.usefixtures("bokeh_backend") def test_stream_popup_selection1d_lasso_select(serve_hv, points):