Dash-Mantine-Components Accordion Behavior

Hi everyone!

I have an accordion in my app that gets populated based on some API calls, and I can “reload” the accordion at any point to see if the title or content of any item changed, which I do by rebuilding the accordion in a callback. When testing this I noticed that some times the refresh kept the state of the accordion exactly as it was before the refresh, and other times it opened or closed the last accordion item I had interacted with. Here’s a video of what I’m trying to say and the code to reproduce it.

accordion-example

from time import sleep
import dash_mantine_components as dmc
from dash import Dash, _dash_renderer, html, Input, Output

_dash_renderer._set_react_version("18.2.0")

app = Dash(
    __name__,
    suppress_callback_exceptions=True,
    external_stylesheets=[dmc.styles.ALL],
)

app.layout = dmc.MantineProvider([
    dmc.Button('Reload', id='reload-accordion'),
    html.Div(id='accordion'),
])


@app.callback(
    Output('accordion', 'children'),
    Input('reload-accordion', 'n_clicks'),
    running=[
        (
            Output('reload-accordion', 'children'),
            [dmc.Loader(size='sm', color='white'), ' Reload'],
            'Reload',
        ),
    ],
    prevent_initial_call=True)
def reload_accordion(_clicks):
    sleep(1)
    return dmc.Accordion([
        dmc.AccordionItem([
            dmc.AccordionControl("A"),
            dmc.AccordionPanel("Content A"),
        ], value="A"),
        dmc.AccordionItem([
            dmc.AccordionControl("B"),
            dmc.AccordionPanel("Content B"),
        ], value="B"),
        dmc.AccordionItem([
            dmc.AccordionControl("C"),
            dmc.AccordionPanel("Content C"),
        ], value="C"),
    ])


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

The only thing I can deduce, although it doesn’t make sense to me, is that when you click items an odd amount of times, it keeps the state of the accordion, and when it’s an even amount of times, it opens or closes the last item you interacted with.

In any case, I was hoping to find a way to control this behavior, mainly, to always keep the state of the accordion after an update. I’m thinking it might be possible because the functionality is already there somewhere, it’s just not consistent.

Thank you in advance for any insight you can provide.

Hi @wmachado

I ran the code you posted and I can’t duplicate the issue - it worked exactly the way you would like it to.

What version of dmc and dash are you running? Another thing to try is to give the dmc.Accordion an id.

i actually ran this and was able to reproduce the error by opening and closing the accordion and then hitting refresh. I think the state is what it was getting hung up on . Right on with AMW’s response, giving a unique id to the accordion that gets regenerated seems to solve the issue.

from time import sleep
import dash_mantine_components as dmc
from dash import Dash, _dash_renderer, html, Input, Output

import uuid

def generate_uuid4():
    return str(uuid.uuid4())

_dash_renderer._set_react_version("18.2.0")

app = Dash(
    __name__,
    suppress_callback_exceptions=True,
    external_stylesheets=[dmc.styles.ALL],
)

app.layout = dmc.MantineProvider([
    dmc.Button('Reload', id='reload-accordion'),
    html.Div(id='accordion'),
])


@app.callback(
    Output('accordion', 'children'),
    Input('reload-accordion', 'n_clicks'),
    running=[
        (
            Output('reload-accordion', 'children'),
            [dmc.Loader(size='sm', color='white'), ' Reload'],
            'Reload',
        ),
    ],
    prevent_initial_call=True)
def reload_accordion(_clicks):
    sleep(1)
    return dmc.Accordion(
        id= generate_uuid4(),
        children=[
        dmc.AccordionItem([
            dmc.AccordionControl("A"),
            dmc.AccordionPanel("Content A"),
        ], value="A"),
        dmc.AccordionItem([
            dmc.AccordionControl("B"),
            dmc.AccordionPanel("Content B"),
        ], value="B"),
        dmc.AccordionItem([
            dmc.AccordionControl("C"),
            dmc.AccordionPanel("Content C"),
        ], value="C"),
    ])



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

Hi @AnnMarieW, @KoalifiedEnough, thanks for the replies!

Giving it a unique id does make it so that everytime you click reload, all accordion items close. That could be achieved also by setting value=None in the accordion.

However, (maybe I dind’t explain myself correctly :sweat_smile:), I want to achieve quite the opposite: to keep whatever the user has opened and closed as is. In my video, the first two tries work like that, but for some reason on the last one for example, you can see I close and open the B item and then hit reload, and the B item automatically closes (I would want for it to stay opened like in the first try).

I know this is supposed to be done with the value of accordion but in my case it’s a bit more complex because I’m trying to apply it to something similar to the file explorer tree (which I saw you contributed to btw @AnnMarieW , thanks :grin:). Maybe it’s not possible to achieve what I want, I just found it weird that it’s almost there by default.

Latest versions, dash==2.18.1 and dash-mantine-components==0.14.6

Here is perhaps a better example:
accordion-example-2

If I open A and then B, a reload results in B getting closed and A opened. I don’t understand why it works like that hahaha but as I said, maybe there’s nothing to do here.

Hi @wmachado
I could replicate your last gif

Try adding both an id and persistence=True to the dmc.Accordion. It worked for me. For real this time! :slight_smile:
dmcaccorionpersistence

Hey that was it! Persistence works for me, thank you @AnnMarieW