snehilvj / dash-mantine-components

Plotly Dash components based on Mantine React Components
https://www.dash-mantine-components.com
MIT License
557 stars 55 forks source link

[bug] Persistence doesn't work on dmc.Select if using grouped data #302

Closed dannyrohde closed 1 month ago

dannyrohde commented 1 month ago

When using the dmc.Select component, persistence works as expected when using a list of dicts with value/label. However, when using groups in the options {'group': 'A', 'items':[{'value':'X', 'label': 'Y'}]| the selected dropdown value does not survive a refresh.

I've narrowed down all other possibilities, but persistence stops working when switching from flat to grouped options.

dannyrohde commented 1 month ago

Currently, my workaround is to set the value manually in a callback, but it's not a great solution as it makes the callback more complicated, and I need to store the selection value myself.

AnnMarieW commented 1 month ago

Hi @dannyrohde

Can you please provide an example that reproduces the issue? The following example works as expected:


import dash_mantine_components as dmc
from dash import Dash, _dash_renderer, Input, Output, callback, html
_dash_renderer._set_react_version("18.2.0")

app = Dash(external_stylesheets=dmc.styles.ALL)

select = html.Div(
    [
        dmc.Select(
            label="Select framework",
            placeholder="Select one",
            id="framework-select",
            value="react",            
            data = [
                {
                    "group": "Frontend",
                    "items": [
                        {"value": "react", "label": "React"},
                        {"value": "angular", "label": "Angular"},
                    ],
                },
                {
                    "group": "Backend",
                    "items": [
                        {"value": "svelte", "label": "Svelte"},
                        {"value": "vue", "label": "Vue"},
                    ],
                },
            ],

            w=200,
            mb=10,
            persistence=True
        ),
        dmc.Text(id="selected-value"),
    ]
)

app.layout = dmc.MantineProvider(select)

@callback(Output("selected-value", "children"), Input("framework-select", "value"))
def select_value(value):
    return value

if __name__ == "__main__":
    app.run(debug=True)
AnnMarieW commented 1 month ago

I'm going to close this now because the I'm unable to reproduce the issue. Please feel free to reopen with a minimal example if you are still facing the same issue.

dannyrohde commented 1 month ago

Edit: @AnnMarieW: Can't seem to be able to reopen the issue. Hope you'll see the comment.

I'm not sure if that will replicate for you, but it seems to recreate the issue on my end.

I need a callback to populate the data items dynamically, and if I change your code to populate the data property dynamically, the value of the dropdown isn't persisted.

Below are two selection boxes, one using a grouped list, one using a flat list. You can select a value from the dropdown. Once you reload the page, the value from the grouped list is reset to empty, but the value from the flat list is persisted.

import dash_mantine_components as dmc
from dash import Dash
from dash import Input
from dash import Output
from dash import _dash_renderer
from dash import callback
from dash import html

_dash_renderer._set_react_version("18.2.0")

app = Dash(external_stylesheets=dmc.styles.ALL)

select = html.Div(
    [
        dmc.Select(
            label="Select framework (grouped)",
            placeholder="Select one",
            id="framework-select-grouped",
            w=200,
            mb=10,
            persistence=True,
        ),
        dmc.Text(id="selected-value-grouped"),
        dmc.Select(
            label="Select framework (flat)",
            placeholder="Select one",
            id="framework-select-flat",
            w=200,
            mb=10,
            persistence=True,
        ),
        dmc.Text(id="selected-value-flat"),
    ]
)

app.layout = dmc.MantineProvider(select)

@callback(
    Output("selected-value-grouped", "children"),
    Input("framework-select-grouped", "value"),
)
def select_value(value):
    return value

@callback(
    Output("selected-value-flat", "children"),
    Input("framework-select-flat", "value"),
)
def select_value(value):
    return value

@callback(
    Output("framework-select-grouped", "data"),
    Input("framework-select-grouped", "children"),
)
def populate_grouped_data(value):
    grouped_data = [
        {
            "group": "Frontend",
            "items": [{"value": "react", "label": "React"}, {"value": "angular", "label": "Angular"}],
        },
        {
            "group": "Backend",
            "items": [{"value": "svelte", "label": "Svelte"}, {"value": "vue", "label": "Vue"}],
        },
    ]
    return grouped_data

@callback(
    Output("framework-select-flat", "data"),
    Input("framework-select-flat", "children"),
)
def populate_flat_data(value):
    flat_data = [
        {"value": "react", "label": "React"},
        {"value": "angular", "label": "Angular"},
        {"value": "svelte", "label": "Svelte"},
        {"value": "vue", "label": "Vue"},
    ]

    return flat_data

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

Before reload:

Screenshot 2024-09-28 at 00 44 07

After reload:

Screenshot 2024-09-28 at 00 44 18

It probably has something to do with the Javascript, but I have no clue how to debug that...

AnnMarieW commented 1 month ago

Hi @dannyrohde

In dash only user input is persisted -not things set dynamically in a callback. Feel free to comment on this issue in the dash GitHub https://github.com/plotly/dash/issues/2678

btw - thanks for the detailed write-up and example - that was really helpful!

Edit @dannyrohde hang on a sec, I may have misunderstood the issue. I'm looking into it.

dannyrohde commented 1 month ago

The data property is populated dynamically (I don't expect this to persist), but the value property is user input, and I'd expect this to persist.

In any case, the only difference between the two select boxes is the grouped vs. flat list. Flat list value survives a reload, group doesn't, everything else being equal. So, given the inconsistent behavior, I'd assume there's a bug somewhere.

Thanks for your minimal example. You did most of the work; I just did some edits :) And thanks for your lightning-fast response :)

AnnMarieW commented 1 month ago

Yes, you are correct - I'll re-open the issue.

AnnMarieW commented 1 month ago
AnnMarieW commented 1 month ago

@dannyrohde Just wanted to let you know that I identified the problem. It's related to the issue that was fixed here https://github.com/snehilvj/dash-mantine-components/pull/301

The fix was applied only to the MultiSelect. It needs to be applied to the Select component as well.

dannyrohde commented 1 month ago

@AnnMarieW Well, what can I say? You're a legend for having found it so quickly.

I had a look at the TS changes to the ComboBox, but I can't quite say I understand what's going on or how to replicate the fix. Looking into the DMC package, I see a lot of Python files and one big JS file, so I assume there is some step in the build process that takes the TS and converts it into something packaged for Python.

In short, I'll be waiting for someone competent to fix it :)

dannyrohde commented 1 month ago

Just updated to 0.14.5 and the bug is gone. Yay. Thanks a lot.

AnnMarieW commented 1 month ago

Yippie! :confetti_ball:

Thanks for confirming :slightly_smiling_face: