sk_dev
sk_dev

Reputation: 325

Vaadin 21: Nesting Scroller into VerticalLayout problem

Given a Composite with 3 nested VerticalLayouts, with full height each
And a Scroller nested into the innermost VerticalLayout,
And the Scroller contains a fourth VerticalLayout with 20 Buttons

When the Composite is rendered

Then I would expect that the outermost VerticalLayout is fully visible on screen
And the VerticalLayouts and the scroller are neatly nested
And the Scroller is as small as required so everything fits on screen
And it is not possible to scroll the outer VerticalLayouts at all (as long as the screen is large enough to hold 4 VerticalLayouts, a Scroller, and at least one Button

But the outermost VerticalLayout does not fit on screen
The Scroller allows scrolling through the Buttons, but the last Button can only be seen when the 3 outer VerticalLayouts are scrolled (which is not supposed to happen).
Scrolling the 3 outer VerticalLayouts shows a weird overlap of the outer VerticalLayouts.

Initial State

initial state

After scrolling the Scroller to the bottom

after scroller scrolling

After scrolling the whole canvas to the bottom

after canvas scrolling

How do I achieve the desired nesting/scrolling behavior?

Here is my code

@Route("3_scrollerx")
public class ScrollerxView extends Composite<Component> {
    @Override
    protected Component initContent() {
        final VerticalLayout lvl1 = new VerticalLayout(new Label("lvl1"));
        final VerticalLayout lvl2 = new VerticalLayout(new Label("lvl2"));
        final VerticalLayout lvl3 = new VerticalLayout(new Label("lvl3"));
        final Scroller scroller = new Scroller();
        scroller.setContent(buildScrollerContent());
        scroller.setHeightFull();

        List.of(lvl1, lvl2, lvl3).forEach(layout -> {
            layout.setHeightFull();
            layout.getStyle().set("border", "1px solid");
        });
        scroller.getStyle().set("border", "1px solid");

        lvl1.add(lvl2);
        lvl2.add(lvl3);
        lvl3.add(scroller); // always weird
        return lvl1;
    }


    private VerticalLayout buildScrollerContent() {
        final var scrollerContent = new VerticalLayout();
        for (int i = 1; i <= 20; i++) {
            scrollerContent.add(new Button("Button " + i));
        }
        scrollerContent.setHeightFull();
        return scrollerContent;
    }
}

Upvotes: 0

Views: 180

Answers (1)

gruntled
gruntled

Reputation: 448

I dealt with this when first creating the main layouts of my Vaadin application. It is actually a styling concern of the flexbox layout, and not specific to Vaadin. The computed minimum height of the VerticalLayout objects is the height of their content. If you set the css min-height: 0 on the vertical layouts it will allow them to be smaller than their content.

You can learn more here:

This kept cropping up as regressions for us as developers forgot to set that min height. VerticalLayout is convenient, but has a lot of theming and styling baked into it for displaying individually controls well. If you are looking for a reusable flexbox component for structuring pages, consider creating a new component.

@Tag("column-layout")
@CssImport("layouts.css")
public class ColumnLayout extends Div {
    public ColumnLayout(Component... components) {
        super(components);
    }
}

With this style:

column-layout {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

Implemented in your example:

@Route("3_scrollerx")
public class ScrollerxView extends Composite<Component> {

    @Override
    protected Component initContent() {
        final ColumnLayout lvl1 = new ColumnLayout(new Label("lvl1"));
        final ColumnLayout lvl2 = new ColumnLayout(new Label("lvl2"));
        final ColumnLayout lvl3 = new ColumnLayout(new Label("lvl3"));

        VerticalLayout scrollerContent = buildScrollerContent();
        List.of(lvl1, lvl2, lvl3, scrollerContent).forEach(layout -> {
            layout.setHeightFull();
            layout.getStyle().set("border", "1px solid");
        });

        lvl1.add(lvl2);
        lvl2.add(lvl3);
        lvl3.add(scrollerContent);
        return lvl1;
    }


    private VerticalLayout buildScrollerContent() {
        final var scrollerContent = new VerticalLayout();
        for (int i = 1; i <= 20; i++) {
            scrollerContent.add(new Button("Button " + i));
        }
        scrollerContent.getStyle().set("overflow", "auto");
        return scrollerContent;
    }
}

Upvotes: 1

Related Questions