Responsive + Dynamic elements within a vertical layout do not maintain size

I feel as though this is a bug, but wanted to first post here to discuss since it is possible I am not using the new layout engine properly. I am struggling to get a responsive DynamicMap to maintain an equal aspect ratio through instantiation and updates. I would like the visualization to fill the available space, but maintain an equal/square aspect ratio (and given we are working on portrait oriented monitors, this really just means fill/scale width).

I have a feeling I might be encountering multiple related issues, but it is not clear to me what I should try to be doing to get the desired effect. Responsive behavior was as expected with panel v0.14.4.

  • panel==1.0.2
  • bokeh==3.1.1
  • holoviews==1.16.0
  • param==1.13.0
  • python==3.10.10

In the following example I place a simple DynamicMap within a template both as-is and contained by different layouts. This is not exhaustive, but captures what I am experiencing at the moment. (I apologize for some of the extra clutter here).

The following is shown (pseudo-code):

  • pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width')
  • pn.pane.HoloViews(hv.DynamicMap)
  • pn.Row(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'))
  • pn.Column(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'))
  • pn.Card(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'))
  • pn.WidgetBox(pn.pane.HoloViews(hv.DynamicMap, sizing_mode='scale_width'))

Example:

# test.py
import holoviews as hv
import numpy as np
import pandas as pd
import panel as pn

hv.extension('bokeh')
pn.extension()

# set up callback to generate 1000 points and stream to the dynamic map
limits = (0, 100)
btn = pn.widgets.Button(name='Update')
def update(event):
    data = pd.DataFrame({
        'x': np.random.randint(*limits, size=1000),
        'y': np.random.randint(*limits, size=1000)
    })
    data_stream.send(data)
btn.on_click(update)

# plot data frame
def plot(data):
    return hv.Points(data, kdims=['x', 'y'])

# set up stream and dynamic map
data_stream = hv.streams.Pipe(data=pd.DataFrame(columns=['x', 'y']))
points = hv.DynamicMap(plot, streams=[data_stream]).opts(
    default_tools=[],  # not needed, but makes scrolling easier when viewing this example
    xlim=limits,  # needed to give initial shape to the canvas
    ylim=limits,  # needed to give initial shape to the canvas
    responsive=True,
    data_aspect=1,
    aspect='square'  # needed, possibly related to https://github.com/holoviz/holoviews/issues/5425
)

hv_pane = pn.pane.HoloViews(
    points,
    sizing_mode='scale_width'
)

hv_pane_no_sizing_mode = pn.pane.HoloViews(
    points
)

pn.template.MaterialTemplate(
    main=[
        btn,
        # ----------
        pn.panel('# Not contained'),
        hv_pane,  # works!
        # ----------
        pn.panel('# Not contained, no sizing mode'),
        hv_pane_no_sizing_mode,  # responsive + update works, but aspect is not respected =(
        # ----------
        pn.panel('# Row Container'),
        pn.Row(
            hv_pane  # works!
        ),
        # ----------
        pn.panel('# Column Container'),
        pn.Column(
            hv_pane  # =(
        ),
        # ----------
        pn.panel('# Card Container'),
        pn.Card(
            hv_pane  # =(
        ),
        # ----------
        pn.panel('# WidgetBox Container'),
        pn.WidgetBox(
            hv_pane  # =(
        )
    ],
    sidebar=[
        pn.panel('# Filler!'),  # used to enable the sidebar so we can dynamically resize by collapsing it
    ]
).servable()
panel serve test.py

An unrelated issue: when you click update for the first time, the plot seems to resize a tad to fit into the space? It is not clear to me why or what space it is now filling, but the plot grows a tiny bit. It is not critical or anything, just something I have observed on my end.

When I am on my own system I will post screen captures.