snehilvj / dash-mantine-components

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

[BUG] Multi-select dropdown "ghosts" after options changed #201

Closed Lxstr closed 4 months ago

Lxstr commented 1 year ago

When a multi-select has one or more selections made but then you change the options available (data prop) to not include those selected, the dropdown is blank. This is suggesting to the user that there is no longer a selection but the values are actually still there in the properties and affecting the outcome.

I think it would be best to either continue showing the values or remove only the values that no longer exist in the new options.

Initially, I was leaning to prefer the second option but I realised there may be situations where the options may be temporarily absent due to a mis-load of options or the user may be playing with different option sets and be annoyed if their selections keep disappearing. Further to this the user may have created their own option using the creatable prop. So I'm thinking it's best to display the selected values even if they are no longer available options and let the user remove them explicitly.

Side note: Initially I thought this issue was related to my use of persistence, but when I did the MRE below, there's no persistence.

import dash_mantine_components as dmc
import dash
from dash import Output, Input, html, callback

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dmc.MultiSelect(
            label="Select frameworks",
            placeholder="Select all you like!",
            id="framework-multi-select",
            value=["ng", "vue"],
            data=[
                {"value": "react", "label": "React"},
                {"value": "ng", "label": "Angular"},
                {"value": "svelte", "label": "Svelte"},
                {"value": "vue", "label": "Vue"},
            ],
            style={"width": 400, "marginBottom": 10},
        ),
        dmc.Text(id="multi-selected-value"),
        dmc.Button(id="change-options", children="Change options"),
    ]
)

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

@callback(
    Output("framework-multi-select", "data"),
    Input("change-options", "n_clicks"),
    prevent_initial_call=True,
)
def select_value(n_clicks):
    return [
        {"value": "swiftui", "label": "SwiftUI"},
        {"value": "stenciljs", "label": "StencilJS"},
        {"value": "flutter", "label": "Flutter"},
    ]

if __name__ == "__main__":
    app.run_server(debug=True)
snehilvj commented 4 months ago

Fixed it. Will be available in the next release. Expected behaviour:

MultiSelect After changing the options, only the values that are also present in the new options are kept, rest are removed from value.

Select After changing the options, if the currently selected value is not part of the new options, then value is set to null.