Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VBox widget not scrollable #790

Open
lopezvoliver opened this issue Sep 16, 2024 · 5 comments
Open

VBox widget not scrollable #790

lopezvoliver opened this issue Sep 16, 2024 · 5 comments
Milestone

Comments

@lopezvoliver
Copy link

lopezvoliver commented Sep 16, 2024

I am trying to build a custom widget class that I want to use for popups in a couple of ipyleaflet Markers. The custom widget consists of a main HBox, which itself contains two VBoxes.. something like this:

image

So far so good.. the smaller VBox on the left is meant to be scrollable. This screenshot was taken from a Jupyter notebook with the ipyleaflet Map as a solara component (see code below).

However, when I test the same code using solara, the left VBox is not scrollable and its elements are incomplete (e.g., you can't see the "C" item):

image

Here's the code:

import solara
import markdown
import ipyleaflet
import ipywidgets as widgets

text = """## Test markdown

### Details
                           
- **A**: a
- **B**: b
- **C**: c

"""

class Map(ipyleaflet.Map):
    def __init__(self, **kwargs):
        
        super().__init__(**kwargs)

        marker = ipyleaflet.Marker(
            location = (0,0),
            draggable = False,
            title = "My marker",
            popup_max_width=1000,
            popup_max_height=1000
        )

        self.add(marker)

        main_box = widgets.HBox(
            layout=widgets.Layout(
                width='1000px',
                height='480px',
                justify_content='space-between',
                )
        )
        panel_box = widgets.VBox(
            layout = widgets.Layout(
                width='25%',
                height='100%',
                border='solid 2px'
            )
        )
        image_box = widgets.VBox(
            layout = widgets.Layout(
                width='65%',
                height='100%',
                border='solid 2px'
            )
        )

        value = markdown.markdown(text)
        description_box = widgets.HTML(
            layout=widgets.Layout(
                border='solid 1px',
                flex_shrink=1
            ),
            value=value,
        )

        panel_box.children = [
            description_box,
            description_box,
            description_box,
            description_box,
        ]

        main_box.children=[
            panel_box,
            image_box
        ]


        marker.popup = main_box


@solara.component
def Page():
    with solara.Column() as main:
        map = Map.element(
            layout=widgets.Layout(height='800px')
        )
    return main


display(Page())
@lopezvoliver
Copy link
Author

Here's what happens if I set overflow='scroll' in the panel_box layout:

image

The horizontal and vertical scroll appear, but they are not enabled (seems like the actual bars are missing).

@iisakkirotko
Copy link
Collaborator

Hi @lopezvoliver! This is a good catch, and seems to directly come from some CSS that we use. I'll look into if this is a version difference, or where we get the CSS from. In the meantime, if you set overflow: auto on both panel_box and description_box it should work correctly in Solara.

For reference the relevant CSS is here

@iisakkirotko
Copy link
Collaborator

So, there are a couple of possible solutions to this:

  1. Changing the order in which we load CSS, in particular, changing the load order in
    {{ pre_rendered_css | safe }}
    {% if vue3 == True %}
    <link href="{{cdn}}/@widgetti/[email protected]/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
    {% else %}
    <link href="{{cdn}}/@widgetti/[email protected]/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
    {% endif %}
    {% if assets.fontawesome_enabled == True %}
    <link rel="stylesheet" href="{{cdn}}{{assets.fontawesome_path}}" type="text/css">
    {% endif %}
    {{ resources.include_css("/static/highlight.css") }}
    {{ resources.include_css("/static/highlight-dark.css") }}
    {{ resources.include_css("/static/assets/style.css") }}

    to
    {{ pre_rendered_css | safe }}
-    {% if vue3 == True %}
-    <link href="{{cdn}}/@widgetti/[email protected]/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
-    {% else %}
-    <link href="{{cdn}}/@widgetti/[email protected]/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
-    {% endif %}


    {% if assets.fontawesome_enabled == True %}
    <link rel="stylesheet" href="{{cdn}}{{assets.fontawesome_path}}" type="text/css">
    {% endif %}
    {{ resources.include_css("/static/highlight.css") }}
    {{ resources.include_css("/static/highlight-dark.css") }}
    {{ resources.include_css("/static/assets/style.css") }}
+    {% if vue3 == True %}
+    <link href="{{cdn}}/@widgetti/[email protected]/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
+    {% else %}
+    <link href="{{cdn}}/@widgetti/[email protected]/dist/main{{ipywidget_major_version}}.css" rel="stylesheet" class="solara-template-css"></link>
+    {% endif %}

This should then match jupyter lab, notebook, and nbconvert in terms of load order.

  1. We could also add the relevant CSS
+ /* <DEPRECATED> */
+ .widget-box, /* </DEPRECATED> */
+ .jupyter-widget-box {
+   box-sizing: border-box;
+   display: flex;
+   margin: 0;
+   overflow: auto;
+ }

to solara/server/assets/style.css.

Our concern is that the first solution could be unexpectedly breaking for some edge cases (similar to this issue). On the plus side, it would align us closer to jupyter, which would make future breakage less likely. I think it might also fix some similar (yet undiscovered) issues. However, as it would be potentially breaking for current users we would probably be inclined to only release that fix in Solara 2.0.

The second solution would introduce some duplicate CSS, and while it fixes this issue, would be more of a bandaid over the underlying cause. We'll have a more in depth discussion about the potential solutions among the maintainers in the beginning of next week, so we'll provide a more concrete plan then. Hopefully the workaround I mentioned in my previous comment is sufficient until then!

@maartenbreddels maartenbreddels added this to the Solara 2.0 milestone Sep 17, 2024
@lopezvoliver
Copy link
Author

lopezvoliver commented Sep 18, 2024

Hi @lopezvoliver! This is a good catch, and seems to directly come from some CSS that we use. I'll look into if this is a version difference, or where we get the CSS from. In the meantime, if you set overflow: auto on both panel_box and description_box it should work correctly in Solara.

For reference the relevant CSS is here

Setting overflow:auto to description_box makes each box have its own scroll, instead of a single scroll for the panel_box.

I don't have a lot of experience with css but I get the feeling that flex-shrink is also involved.

I prepared a pycafe example here where I set the overflow and optionally the flex-shrink properties using css.

Here is what happens when using css_fix_1 (only updating overflow to auto):

image

Each description_box gets a scrollbar -- somehow flex-shrink is set to a non-zero value automatically?

Here's the result using css_fix_2 (which also updates flex-shrink to 0)):

image

But using flex_shrink=0 through widgets.Layout doesn't work.. so this is probably something to look at as well.

@iisakkirotko
Copy link
Collaborator

iisakkirotko commented Sep 18, 2024

You're correct, I made a mistake in my post. On description_box you should have overflow: unset instead of auto, see this py.cafe snippet.

Edit: overflow: visible also works

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

No branches or pull requests

3 participants