mckinsey / vizro

Vizro is a toolkit for creating modular data visualization applications.
https://vizro.readthedocs.io/en/stable/
Apache License 2.0
2.61k stars 116 forks source link

[Feat] Apply custom CSS to AgGrid #268

Closed maxschulz-COL closed 6 months ago

maxschulz-COL commented 7 months ago

Description

This PR extends https://github.com/mckinsey/vizro/pull/260 to include styling. It is kept separate in order to keep PRs small.

The target designs can be found below:

image image image image

Open tasks

@nadijagraca Could you take a look at the last two items?

On the grid extension and scrolling behaviour

@AnnMarieW made a further observation about the scrolling behaviour. @huong-li-nguyen I summarized my current understanding below:

The requirements for the AG Grid are:

  1. the AG Grid should extend to the full size of the container, horizontally and vertically
  2. if the columns naturally extend to the horizontal width (ie there is enough of them, or the content is very long), then a horizontal scroll bar should appear
  3. in the above case, the column width should be determined by the column content
  4. if the columns do not extend to the horizontal width, they should be stretched to do so
  5. if the columns naturally extend to the vertical height (ie there are more rows than fit on the screen), then a vertical scroll bar should appear
  6. Everything of the above should happen, but when users resize the window, the table resizes as well AND the column width proportions are kept

The current code does:

  1. yes
  2. yes, but it appears “out of sight” ie only visible if scrolled to bottom
  3. no
  4. yes
  5. yes, but the header disappears as we are scrolling the container
  6. yes

Originally I was only concerned to make 3 happen on top of everything else. With your @AnnMarieW observation, the caveats on 2 and 5 are now also an issue.

Do we think we can solve all 6 points at the same time? Or at least all but 3?

I am starting to think that we may want to loose 6 (ie the proportions are not kept, and just use responsizeSizetoFit)?

Screenshot

Notice

AnnMarieW commented 7 months ago

Hi @maxschulz-COL

Just a heads up that AG Grid supports creating an entirely new theme using Figma. You can find more information here: https://ag-grid.com/react-data-grid/ag-grid-design-system/

Alternately, you can get a long way by starting with the ag-theme-alpine and customizing the selectors needed to match the theme.

For example, here are the selectors I use to style the grid for light and dark mode in any Bootstra theme.

.dbc-ag-grid .ag-theme-alpine {
  --ag-alpine-active-color: var(--bs-primary);
  --ag-selected-row-background-color: rgba(var(--bs-primary-rgb), 0.3);
  --ag-row-hover-color: rgba(var(--bs-primary-rgb), 0.1);
  --ag-column-hover-color: rgba(var(--bs-primary-rgb), 0.1);
  --ag-input-focus-border-color: rgba(var(--bs-primary-rgb), 0.4);
  --ag-range-selection-background-color: rgba(var(--bs-primary-rgb), 0.2);
  --ag-range-selection-background-color-2: rgba(var(--bs-primary-rgb), 0.36);
  --ag-range-selection-background-color-3: rgba(var(--bs-primary-rgb), 0.49);
  --ag-range-selection-background-color-4: rgba(var(--bs-primary-rgb), 0.59);
  --ag-background-color: var(--bs-body-bg);
  --ag-foreground-color: var(--bs-body-color);
  --ag-border-color:  rgba(173,181,189, 0.40);
  --ag-cell-horizontal-border:  rgba(173,181,189, 0.20);
  --ag-secondary-border-color:  rgba(173,181,189, 0.20);
  --ag-header-background-color:  rgba(173,181,189, 0.20);
  --ag-odd-row-background-color:  rgba(173,181,189, 0.05);
  --ag-control-panel-background-color: var(--bs-body-bg);
  --ag-subheader-background-color:  var(--bs-body-bg);
  --ag-invalid-color: var(--bs-form-invalid-color);
  --ag-font-family: var(--bs-font-family);
  --ag-tooltip-background-color: var(--bs-gray-400);
}

Here is a start with some Vizro variables to make it work in light and dark:


 .ag-theme-alpine.vizro {
  --ag-background-color: var(--main-container-bg-color);
  --ag-foreground-color: var(--text-primary);
  --ag-border-color:  var(--main-container-bg-color);
  --ag-cell-horizontal-border:  var(--border-subtle-alpha-01);
  --ag-secondary-border-color:  var(--border-subtle-alpha-01);
  --ag-header-background-color:  var(--main-container-bg-color);
  --ag-odd-row-background-color:  var(--main-container-bg-color);
  --ag-control-panel-background-color: var(--main-container-bg-color);
  --ag-subheader-background-color:  var(--main-container-bg-color);
  --ag-row-height: 40px;
  --ag-header-height: 30px;
}

.ag-theme-alpine .ag-header {
    border-bottom: 1px solid var(--border-subtle-alpha-01);
}

Note that the className on the AG Grid component needs to include the base theme (Alpine) like this:

def dash_ag_grid(data_frame=None, **kwargs):
    """Custom AgGrid."""
    return dag.AgGrid(
        className= "ag-theme-alpine vizro",
        rowData=data_frame.to_dict("records"),
       # columnDefs=[{"field": col} for col in data_frame.columns],    Needs to be set as a default prop like in DataTable
        **kwargs
    )
AnnMarieW commented 7 months ago

Also, if you would like to change the demo app to use the grid instead of DataTable, here is how to add the conditional formatting and the number formatting:

cellStyle = {
        "styleConditions": [
            {
                "condition": "params.value < 1045",
                "style": {"backgroundColor": "#ff9222"},
            },
            {
                "condition": "params.value >= 1045 && params.value <= 4095",
                "style": {"backgroundColor": "#de9e75"},
            },
            {
                "condition": "params.value > 4095 && params.value <= 12695",
                "style": {"backgroundColor": "#aaa9ba"},
            },
            {
                "condition": "params.value > 12695",
                "style": {"backgroundColor": "#00b4ff"},
            },
        ]
    }

    columnDefs = [
        {"field": "country"},
        {"field": "continent"},
        {"field": "year"},
        {
            "field": "lifeExp",
            "type": "rightAligned",
            "valueFormatter": {"function": "d3.format('.1f')(params.value)"},
        },
        {
            "field": "gdpPercap",
            "type": "rightAligned",
            "valueFormatter": {"function": "d3.format('$,.1f')(params.value)"},
            "cellStyle": cellStyle
        },
        {
            "field": "pop",
            "type": "rightAligned",
            "valueFormatter": {"function": "d3.format(',.0f')(params.value)"},
        },
    ]

    page_country = vm.Page(
        title="Benchmark Analysis",
        # description="Discovering how the metrics differ for each country and export data for further investigation",
        # layout=vm.Layout(grid=[[0, 1]] * 5 + [[2, -1]], col_gap="32px", row_gap="60px"),
        components=[
            vm.Table(
                id="table_country_new",
                title="Click on a cell in country column:",
                figure=dash_ag_grid(
                    id="dash_ag_grid_country",
                    data_frame=df,
                    columnDefs=columnDefs,
                    defaultColDef={"resizable": True, "sortable": True},
                    columnSize="sizeToFit",
                ),
                actions=[vm.Action(function=filter_interaction(targets=["line_country"]))],
            ),
AnnMarieW commented 7 months ago

Open tasks -- here are some links that might be helpful:

maxschulz-COL commented 7 months ago

Hi @maxschulz-COL

Just a heads up that AG Grid supports creating an entirely new theme using Figma. You can find more information here: https://ag-grid.com/react-data-grid/ag-grid-design-system/

Amazing, this should be the approach for our designer once we have the development capacity for it. I will pass it on!

Alternately, you can get a long way by starting with the ag-theme-alpine and customizing the selectors needed to match the theme.

Thanks for the approach, I will also look into this one. In the docs there was the mention

If your desired look and feel is very different from the provided theme, at some point it becomes easier to start from scratch

so we opted for a new theme. Do you think that is pushing it too far?

AnnMarieW commented 7 months ago

so we opted for a new theme. Do you think that is pushing it too far?

Haha - yes. Creating an entirely new theme is certainly possible, and might be easy if you have phd in CSS :sweat_smile: . But seriously, if that's what you want to do, then the Figma process might be the way to go.

There is a lot of CSS necessary to make the grid function correctly, and I think the design you posted would be achievable by customizing the Alpine theme (except for the column gap in the borders - I have no clue how you would do that).

maxschulz-COL commented 7 months ago

so we opted for a new theme. Do you think that is pushing it too far?

Haha - yes. Creating an entirely new theme is certainly possible, and might be easy if you have phd in CSS 😅 . But seriously, if that's what you want to do, then the Figma process might be the way to go.

There is a lot of CSS necessary to make the grid function correctly, and I think the design you posted would be achievable by customizing the Alpine theme (except for the column gap in the borders - I have no clue how you would do that).

@huong-li-nguyen Shall we revert from the new theme then and try to adjust the existing alpine theme?

maxschulz-COL commented 7 months ago

There is a lot of CSS necessary to make the grid function correctly, and I think the design you posted would be achievable by customizing the Alpine theme (except for the column gap in the borders - I have no clue how you would do that).

@AnnMarieW That seems a convincing argument to not create our own theme. Didn't know that the functionality was also being compromised. Will definitely look into this

maxschulz-COL commented 7 months ago

There is a lot of CSS necessary to make the grid function correctly, and I think the design you posted would be achievable by customizing the Alpine theme (except for the column gap in the borders - I have no clue how you would do that).

@AnnMarieW That seems a convincing argument to not create our own theme. Didn't know that the functionality was also being compromised. Will definitely look into this

@huong-li-nguyen @AnnMarieW I have based the design on the alpine theme now - works well and has the added benefit that previously not designed elements such as the filter fly out look better now 💪