'Description tooltips for Vaadin Grid header cells

I'd like to define descriptions for Grid header cells, similarly to how AbstractComponent.setDescription(String description) works (i.e. tooltip shown on mouse hover). As the Grid doesn't support this in itself, I tried adding a Label component into the header cell, and then use setDescription() on the label. I can get the info tooltip working like this, but the downside is that clicking on the label component doesn't trigger sorting. If I want to sort the column, I need to click the header cell on the really narrow area that's left between the right edge of the label component and the column border, where the sorting indicator will be shown. If you look at the screenshot below, the highlighted area is the label component, and in order to trigger sorting, the user needs to click on the space on the right side of the component.

Example Grid

Is there a better way to apply descriptions to header cells than the one I described? And if not, is there a way to make the sorting work properly when the header cell contains a Component?



Solution 1:[1]

Based on the answer from kukis, I managed to come up with a simpler solution that doesn't require any JavaScript. Instead of adding a Label component into the header cell, I'm adding a div element manually with StaticCell.setHtml(), and setting the title attribute on it:

@Override
protected void init(VaadinRequest request) {
    Grid grid = new Grid();
    grid.addColumn("to");
    grid.addColumn("the");
    grid.addColumn("moon");

    Grid.HeaderRow headerRow = grid.getDefaultHeaderRow();
    headerRow.getCell("to").setHtml("<div title='Hello world'>to</div>");
    headerRow.getCell("the").setHtml("<div title='Hello world 2'>the</div>");
    headerRow.getCell("moon").setHtml("<div title='Hello world 3'>moon</div>");

    grid.addRow("1","2","3");
    grid.addRow("d","v","w");
    grid.addRow("g","s","h");

    setContent(new VerticalLayout(grid));
}

Solution 2:[2]

Well, since Grid doesn't support it by itself you can always use JavaScript to achieve desired behaviour. SSCCE:

private final String SCRIPT;
{
    StringBuilder b = new StringBuilder();
    b.append("var grid = document.getElementById('mygrid');\n");
    b.append("var child = grid.getElementsByClassName('v-grid-tablewrapper')[0];\n");
    b.append("child = child.firstChild.firstChild.firstChild;\n");
    b.append("child.childNodes[0].title='Hello world';\n");
    b.append("child.childNodes[1].title='Hello world 2';\n");
    b.append("child.childNodes[2].title='Hello world 3';\n");
    SCRIPT = b.toString();
}

@Override
protected void init(VaadinRequest request) {
    final VerticalLayout layout = new VerticalLayout();
    Grid grid = new Grid();
    grid.addColumn("to");
    grid.addColumn("the");
    grid.addColumn("moon");
    grid.addRow("1","2","3");
    grid.addRow("d","v","w");
    grid.addRow("g","s","h");
    grid.setId("mygrid");
    setContent(layout);
    layout.addComponent(grid);
    JavaScript.getCurrent().execute(SCRIPT);
}

Another possibility would be to develop your own Grid in GWT based on the Grid provided by Vaadin Team but it is a way higher cost approach.

Another solution would be to, as you have tried, put label in a column and propagate the label-clicked-event to the Grid.

Solution 3:[3]

I use my own utillity finction:

public static Grid setHeaderCellDescription(Grid grid, int rowIndex, String property, String description) {
    Grid.HeaderCell cell;
    String cellHeader = "<span title=\"%s\">%s</span>";
    cell = grid.getHeaderRow(rowIndex).getCell(property);
    cell.setHtml(String.format(cellHeader, description, cell.getText()));
    return grid;
}

You may add some additional checks if need (existing of cell and row number).

Or other variant - instead setHtml use cetComponent.

Grid.HeaderCell cell = grid.getHeaderRow(rowIndex).getCell(property);
Label newLabel = new Label(cell.getText());
newLabel.setDescription(description);
cell.setComponent(newLabel);

Solution 4:[4]

Update for Vaadin 23: you can use your own Component as a column header with this method: com.vaadin.flow.component.grid.Grid.Column#setHeader(com.vaadin.flow.component.Component).

So you can use e.g. a Span with a title:

Span headerComponent = new Span();
headerComponent.setText("Your header text");
headerComponent.getElement().setProperty("title", "Your tooltip text");
column.setHeader(headerComponent);

Solution 5:[5]

move = input(": ")

This is where you have done the mistake. When you get the input like this, the value is considered as type "String". (https://docs.python.org/3/library/functions.html#input)

Use,

move = int(input(":"))

as you have done in other places like getting the input for stock

Solution 6:[6]

Move needs to be an integer, but it is a string when returned by the input function, so currently none of the conditions evaluate as true. You could make the if statement work by defining move as: move = int(input(": ")).

import random

amount = 0
cash = 1000
y = int(input("How many days would you like to play ? : "))
x = 0
move = 0

while x < y:
    price = int(random.randint(50 , 1000))
    print("Stock Price:" )
    print (price )
    print("Cash: ")
    print(cash)
    print("Stocks Owned: ")
    print(amount)
    print("Days Remaining: ")
    print(x)
    print("What do you want to do? 1 to buy stocks, 2 to sell stocks, 3 to skip a day.")
    #taking the input from user
    move = int(input(": "))

    if move == 1:
        stock = int(input("how many stocks would you like to buy? : "))
        amount = (price * stock)
        cash = cash - (price * stock)
        x = x + 1
        print("--------------------------------------------------------------------------------------------------")
    elif move == 2:
        cash = cash + (amount * price)
        amount = 0
        x = x + 1
        print("--------------------------------------------------------------------------------------------------")
    elif move == 3:
        x = x + 1
        print("--------------------------------------------------------------------------------------------------")

print(f"You made ${cash} in {y} days.")
print(f"Score: {cash / y}")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Markus Yrjölä
Solution 2 kukis
Solution 3
Solution 4 S. Doe
Solution 5 Ihor Konovalenko
Solution 6