Column Groups

Grouping columns allows you to have multiple levels of columns in your header and the ability, if you want, to
‘open’/’close’ column groups to show/hide additional columns.

To group columns, provide them in a tree hierarchy to the grid. There is no limit to the number of levels you can
provide.

Here is how to create two groups of columns:

columnDefs= [
        {
            'headerName': 'Athlete Details',
            'children': [
                { 'field': 'athlete' },
                { 'field': 'age' },
                { 'field': 'country' },
            ]
        },
        {
            'headerName': 'Sports Results',
            'children': [
                { 'field': 'sport' },
                { 'field': 'total', 'columnGroupShow': 'closed' },
                { 'field': 'gold', 'columnGroupShow': 'open' },
                { 'field': 'silver', 'columnGroupShow': 'open' },
                { 'field': 'bronze', 'columnGroupShow': 'open' },
            ]
        }
    ]

Here is a simple example showing a column groups configuration.

import dash_ag_grid as dag
from dash import Dash, html
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {
        "headerName": "Athlete Details",
        "children": [
            {"field": "athlete", "width": 180},
            {"field": "age", "width": 90},
            {"field": "country", "width": 140},
        ],
    },
    {
        "headerName": "Sports Results",
        "children": [
            {"field": "sport", "width": 140},
            {"field": "total", "width": 100, "filter": "agNumberColumnFilter", "columnGroupShow": "closed"},
            {"field": "gold", "width": 100, "filter": "agNumberColumnFilter", "columnGroupShow": "open"},
            {"field": "silver", "width": 100, "filter": "agNumberColumnFilter", "columnGroupShow": "open"},
            {"field": "bronze", "width": 100, "filter": "agNumberColumnFilter", "columnGroupShow": "open"},
        ],
    },
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="column-groups-basic",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            defaultColDef={"filter": True},
            dashGridOptions={"animateRows": False}
        ),
    ]
)

if __name__ == "__main__":
    app.run(debug=True)

Column Definitions vs Column Group Definitions

The list of columns in columnDefs can be a mix of columns and column groups. Each level can have any number of columns
and column groups and in any order. The difference in column vs column group definitions is as follows:

Show / Hide Columns

A group can have children shown or hidden based on the open/closed state of the group. This is controlled by
setting columnGroupShow on one or more of the children. When a child has columnGroupShow set, it behaves in the
following way:

If a group has any child with columnGroupShow set as 'open'/'closed', then the open/close icon will appear in
the group header. Otherwise, the icon will not be shown.

Having columns only shown when closed is useful when you want to replace a column with others. For example, in the code
snippet above (and the example below), the Total column is replaced with other columns when the group is opened.

If a group has an ‘incompatible’ set of children, then the group opening/closing will not be activated. An
incompatible set is one which will have no columns visible at some point (i.e. all are set to 'open' or 'closed').

Pin and Move Columns with Groups

Pinned columns break groups. So if you have a group with 10 columns, 4 of which are inside the pinned area, two groups
will be created, one with 4 (pinned) and one with 6 (not pinned).

If you move columns so that columns in a group are no longer adjacent, then the group will again be broken and displayed
as one or more groups in the grid.

Resize Groups

  1. If you grab the group resize bar, it resizes each child in the group, evenly distributing the new additional width.
  2. If you grab the child resize bar, only that one column will be resized.

Resizing Column Groups

Color Groups

The grid doesn’t color the groups for you. However, you can use the column definition headerClass for this purpose.
The headerClass attribute is available on both columns and column groups.

columnDefs = [
    # the CSS class name supplied to 'headerClass' will get applied to the header group
    {"headerName": 'Athlete Details', "headerClass": 'my-css-class', "children": []}
]

Align the Group Header Label

The labels in the group headers are aligned to the left by default. To make the group headers center-aligned or
right-aligned, it is possible to set the CSS property justify-content applied to the AG Grid
class .ag-header-group-cell-label.

The following example shows how to set the header groups alignment.

View the CSS classes used for this example

These CSS classes must be added to any *.css file in the assets folder.
See Loading CSS files for more information.

.center-aligned-group-header .ag-header-group-cell-label {
    justify-content: center;
}

.right-aligned-group-header .ag-header-group-cell-label {
    justify-content: right;
}
import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output, State, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {
        "headerName": "Athlete Details",
        'headerClass': 'center-aligned-group-header',
        "children": [
            {"field": "athlete", "width": 180},
            {"field": "age", "width": 90},
            {"field": "country", "width": 140},
        ],
    },
    {
        "headerName": "Sports Results",
        'groupId': 'sports-results',
        'headerClass': 'center-aligned-group-header',
        "children": [
            {"field": "sport", "width": 140},
            {"field": "total", "width": 100, "columnGroupShow": "closed"},
            {"field": "gold", "width": 100, "columnGroupShow": "open"},
            {"field": "silver", "width": 100, "columnGroupShow": "open"},
            {"field": "bronze", "width": 100, "columnGroupShow": "open"},
        ],
    },
]

app.layout = html.Div(
    [
        dcc.RadioItems(
            value='center-aligned-group-header', inline=True, id='radio-column-groups-label-align',
            options=[
                {'label': 'Left', 'value': ''},
                {'label': 'Center', 'value': 'center-aligned-group-header'},
                {'label': 'Right', 'value': 'right-aligned-group-header'},
            ]
        ),
        dag.AgGrid(
            id="column-groups-label-align",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            defaultColDef={"filter": True},
            dashGridOptions={"animateRows": False}
        ),
    ]
)


@callback(
    Output("column-groups-label-align", "columnDefs"),
    Input("radio-column-groups-label-align", "value"),
    State("column-groups-label-align", "columnDefs"),
    prevent_initial_call=True,
)
def change_header_align(align_class, col_def):
    for group in col_def:
        group['headerClass'] = align_class
    return col_def


if __name__ == "__main__":
    app.run(debug=True)

Marry Children

Sometimes you want columns of the group to always stick together. To achieve this, set the column group
property marryChildren=True. The example below demonstrates the following:

View the CSS classes used for this example

These CSS classes must be added to any *.css file in the assets folder.
See Loading CSS files for more information.

.column-groups-header-background-color .ag-header-group-cell-with-group {
    background-color: #00e7b1 !important;
}
import dash_ag_grid as dag
from dash import Dash, html
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {
        "headerName": "Athlete Details",
        "marryChildren": True,
        "children": [
            {"field": "athlete"},
            {"field": "country"},
        ],
    },
    {"field": "age"},
    {
        "headerName": "Sports Results",
        "marryChildren": True,
        "children": [
            {"field": "sport"},
            {"field": "total"},
        ],
    },
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="column-groups-marrychildren",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            columnSize="sizeToFit",
            dashGridOptions={"animateRows": False}
        ),
    ],
    className="column-groups-headers",
)

if __name__ == "__main__":
    app.run(debug=True)

Suppressing Sticky Label

When Column Groups are too wide, the Header Label is always visible while scrolling the grid horizontally. If you want
to supress this, set the column group property suppressStickyLabel=True. The example below demonstrates the following:

import dash_ag_grid as dag
from dash import Dash, html
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {
        "headerName": "Athlete Details",
        "children": [
            {"field": "athlete", "pinned": True},
            {"field": "country"},
            {"field": "age"},
        ],
    },
    {
        "headerName": "Sports Results",
        "suppressStickyLabel": True,
        "openByDefault": True,
        "children": [
            {"field": "sport"},
            {"field": "gold", "columnGroupShow": "open"},
            {"field": "silver", "columnGroupShow": "open"},
            {"field": "bronze", "columnGroupShow": "open"},
            {"field": "total", "columnGroupShow": "closed"},
        ],
    },
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="column-groups-sticky-label",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            defaultColDef={"width": 200},
            dashGridOptions={"animateRows": False}
        )
    ],
)

if __name__ == "__main__":
    app.run(debug=True)

Group Changes

Similar to adding and removing columns, you can also add and remove column groups. If the column definitions passed in
have column groups, then the columns will be grouped to the new configuration.
The example below shows adding and removing groups to columns.

View the CSS classes used for this example

These CSS classes must be added to any *.css file in the assets folder.
See Loading CSS files for more information.

.column-groups-participant-group {
    background-color: #119dff !important;
}

.column-groups-medals-group {
    background-color: #66c2a5 !important;
}
import dash_ag_grid as dag
from dash import Dash, html, Input, Output, ctx, callback
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

participant = [
    {"field": "athlete"},
    {"field": "age"},
    {"field": "country"},
]

medals = [
    {"field": "total"},
    {"field": "gold"},
    {"field": "silver"},
    {"field": "bronze"},
]

noGroups = participant + medals

participantInGroupOnly = [
    {
        "headerName": "Participant",
        "headerClass": "column-groups-participant-group",
        "children": participant,
    },
    *medals
]
medalsInGroupOnly = [
    *participant,
    {
        "headerName": "Medals",
        "headerClass": "column-groups-medals-group",
        "children": medals,
    },
]
participantAndMedalsInGroups = [
    {
        "headerName": "Participant",
        "headerClass": "column-groups-participant-group",
        "children": participant,
    },
    {
        "headerName": "Medals",
        "headerClass": "column-groups-medals-group",
        "children": medals,
    },
]

app.layout = html.Div(
    [
        html.Button("No Groups", id="btn-no-groups"),
        html.Button("Participants in Group", id="btn-participants-in-group"),
        html.Button("Medals in Group", id="btn-medals-in-group"),
        html.Button("Participants and Medals in Group", id="btn-participants-and-medals-in-group"),
        dag.AgGrid(
            id="column-group-changes",
            rowData=df.to_dict("records"),
            columnDefs=noGroups,
            defaultColDef={"filter": True, 'initialWidth': 150},
            dashGridOptions={"animateRows": False}
        ),
    ],
)


@callback(
    Output("column-group-changes", "columnDefs"),
    Input("btn-no-groups", "n_clicks"),
    Input("btn-participants-in-group", "n_clicks"),
    Input("btn-medals-in-group", "n_clicks"),
    Input("btn-participants-and-medals-in-group", "n_clicks"),
    prevent_initial_call=True,
)
def update(*_):
    if ctx.triggered_id == "btn-participants-and-medals-in-group":
        return participantAndMedalsInGroups
    if ctx.triggered_id == "btn-participants-in-group":
        return participantInGroupOnly
    if ctx.triggered_id == "btn-medals-in-group":
        return medalsInGroupOnly
    return noGroups


if __name__ == "__main__":
    app.run(debug=True)

Suppress Span Header Height

By default, the grid will resize the header cell to span the whole height of the header container. Using the Column
Property suppressSpanHeaderHeight the Grid will balance the column headers with different number of levels with an
empty column group header cell.

The example below shows when suppressSpanHeaderHeight is used. Note the following:

View the CSS classes used for this example

These CSS classes must be added to any *.css file in the assets folder.
See Loading CSS files for more information.

.column-groups-headers .ag-header-group-cell-with-group {
    background-color: #119dff !important;
}
import dash_ag_grid as dag
from dash import Dash, html
import pandas as pd

app = Dash(__name__)

df = pd.read_csv(
    "https://raw.githubusercontent.com/plotly/datasets/master/ag-grid/olympic-winners.csv"
)

columnDefs = [
    {
        "headerName": "Athlete Details",
        "children": [
            {"field": "athlete"},
            {"field": "country"},
        ],
    },
    {"field": "age"},
    {"field": "year", "suppressSpanHeaderHeight": True},
    {
        "headerName": "Sports Results",
        "children": [
            {"field": "sport"},
            {"field": "total"},
        ],
    },
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="column-groups-span-header",
            rowData=df.to_dict("records"),
            columnDefs=columnDefs,
            columnSize="sizeToFit",
            dashGridOptions={"animateRows": False}
        ),
    ],
    className="column-groups-headers",
)

if __name__ == "__main__":
    app.run(debug=True)