dbc.Checklist in dmc.Accordition

I’m trying to use dbc.Checklist to filter a dash.data_table and I want to make it looks cleaner so I decided to add Checklist in dmc.Accordition. Something as below:

from dash import Dash, dash_table
import pandas as pd
import dash_mantine_components as dmc
import dash_bootstrap_components as dbc
from dash.dependencies import Input ,Output
df = pd.read_csv('https://raw.githubusercontent.com/hoatranobita/holiday_challenge/main/solar.csv')

app = Dash(__name__, external_stylesheets=[dbc.themes.LUX])

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            dmc.Accordion(
                children=[
                    dmc.AccordionItem([
                        dmc.AccordionControl("State"),
                        dmc.AccordionPanel([
                            dbc.Checklist(
                                options=[{"label": x, "value": x} for x in df['State'].unique()],
                                value=[],
                                id="checklist-input"),
                            html.Div(id='city')]),
                    ],
                        value="customization",
                    ),
                ]),
        ],width=2),
        dbc.Col([
            dash_table.DataTable(df.to_dict('records'), 
                                 columns = [{"name": i, "id": i} for i in df.columns],
                                 id='tbl')
        ],width=10)
    ], className='p-2 align-items-center')
],fluid=True)

@app.callback(Output('city','children'),
              Input('checklist-input','value'))

def update_table(checklist):
    if checklist:
        dff = df[df['State'].isin(checklist)]
        return dmc.Accordion(
            children=[
                dmc.AccordionItem([
                    dmc.AccordionControl("City"),
                    dmc.AccordionPanel([
                        dbc.Checklist(
                            options=[
                                {"label": x, "value": x} for x in dff['City'].unique()
                            ],
                            value=[],
                            id="checklist-input-2")
                    ])
                ],
            value="customization2")
            ])


@app.callback(Output('tbl','data'),
              [Input('checklist-input','value'),
               Input('checklist-input-2','value')],
             )

def update_table(checklist, checklist2):
    if checklist != [] and checklist2 != []:
        dff = df[df['State'].isin(checklist)]
        dff = dff[dff['City'].isin(checklist2)]
        return dff.to_dict(orient='records')
    elif checklist != [] and checklist2 == []:
        dff = df[df['State'].isin(checklist)]
        return dff.to_dict(orient='records')   
    else:
        return df.to_dict(orient='records')
    
if __name__ == '__main__':
    app.run_server(debug=False)

But I have 2 questions as below:

  • Is there any way to add html.Div (City Checklist) right under each first checklist. For example, when I choosing California, City Checklist will be under of it.
  • How to keep the second checklist not be refreshed when I choose both of the first Checklist.

Thank you.

HI @hoatran this looks similar to the file explorer, have you seen this topic?

1 Like

Thank you, it seems not easy thing to understand :smiling_face_with_tear:

@hoatran, man, this one had me thinking a while.

In the end I used pattern machting callbacks and a helper function. It’s not perfect as the City- Accordion is still visible even if no State is chosen- this only works at startup. Maybe you can alter the visibility in the callback. Feel free to ask for clarification as this is maybe not best coding style :see_no_evil:

from dash import Dash, dash_table, html, Input, Output, MATCH, ALL, ctx
import pandas as pd
import dash_mantine_components as dmc
import dash_bootstrap_components as dbc

df = pd.read_csv('https://raw.githubusercontent.com/hoatranobita/holiday_challenge/main/solar.csv')


def create_panel(idx, state):
    return dmc.AccordionPanel(
        [
            dbc.Checklist(
                options=[{"label": state, "value": state}],
                value=[],
                id={'type': "checklist-input", 'index': idx}
            ),
            html.Div(id={'type': "city", 'index': idx})
        ]
    )


app = Dash(__name__, external_stylesheets=[dbc.themes.LUX])

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col(
            [
                dmc.Accordion(
                    children=[
                        dmc.AccordionItem(
                            [dmc.AccordionControl("State")] +
                            [create_panel(idx, state) for idx, state in enumerate(df['State'].unique())],
                            value="customization",
                        ),
                    ]
                ),
            ],
            width=2
        ),
        dbc.Col([
            dash_table.DataTable(
                df.to_dict('records'),
                columns=[{"name": i, "id": i} for i in df.columns],
                id={'type': 'tbl', 'index': 0}
            ),
        ], width=10)
    ], className='p-2 align-items-center')
], fluid=True)


@app.callback(
    Output({'type': 'city', 'index': MATCH}, 'children'),
    Input({'type': 'checklist-input', 'index': MATCH}, 'value'),
    prevent_initial_call=True
)
def update_table(checklist):
    # get the triggering index to make sure the indices of checklist-input and checklist-input-2
    # are the same
    idx = ctx.triggered_id['index']

    dff = df[df['State'].isin(checklist)]
    return dmc.Accordion(
        children=[
            dmc.AccordionItem([
                dmc.AccordionControl("City"),
                dmc.AccordionPanel([
                    dbc.Checklist(
                        options=[
                            {"label": x, "value": x} for x in dff['City'].unique()
                        ],
                        value=[],
                        id={'type': 'checklist-input-2', 'index': idx})
                ])
            ],
                value="customization2")
        ])


@app.callback(
    Output({'type': 'tbl', 'index': ALL}, 'data'),
    Input({'type': 'checklist-input', 'index': ALL}, 'value'),
    Input({'type': 'checklist-input-2', 'index': ALL}, 'value'),
    prevent_initial_call=True
)
def update_table(checklist, checklist2):
    # flatten checklist and checklist2 - due to using pattern matching callbacks with wildcard ALL
    # these are a list of lists
    cl_flat = [item for sublist in checklist for item in sublist]
    cl2_flat = [item for sublist in checklist2 for item in sublist]

    if cl_flat != [] and cl2_flat != []:
        dff = df[df['State'].isin(cl_flat)]
        dff = dff[dff['City'].isin(cl2_flat)]
        return [dff.to_dict(orient='records')]

    elif cl_flat != [] and cl2_flat == []:
        dff = df[df['State'].isin(cl_flat)]
        return [dff.to_dict(orient='records')]
    else:
        return [df.to_dict(orient='records')]


if __name__ == '__main__':
    app.run_server(debug=True)

table

mred accordion

3 Likes

@AIMPED : Thank you so much, you made my day :grin:. I will use it in my future projects.

1 Like