@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 ![]()
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)

mred accordion