snehilvj / dash-mantine-components

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

Broken dmc.MultiSelect in 0.14.4 #291

Closed MichaelODeli closed 1 month ago

MichaelODeli commented 2 months ago

Hello. Before we start, I would like to thank the developer for porting such a beautiful library to the Dash platform.

The problem is the "broken" dmc.MultiSelect component in version 0.14.4.

The design code is:

dmc.MultiSelect(
w="47%",
searchable=True,
hidePickedOptions=True,
clearable=True,
label="Types to search",
id="n_search_in_types",
placeholder="Search all types",
disabled=True,
value=[])

Callback code

@callback(
    Output("n_search_in_types", "data"),
    Output("n_search_in_types", "disabled"),
    Output("n_search_in_category", "placeholder"),
    Output("n_search_in_types", "placeholder"),
    Input("n_search_in_category", "value"),
    State("n_search_in_types", "value"), # or Input, doesn't matter
)
def add_types_in_search(category_id, selected_types):
# some work code
return (
            types_select_data, # dict with values
            False,
            "Search by categories",
            (
                "Search by selected types"
                if len(selected_types) > 0
                else "Search by all types"
            ),
        )

The logic of the callback is to get the value of the n_search_in_category field from the user and suggest suitable types corresponding to the given category. In version 0.14.3 this worked as follows: get the value from dmc.MultiSelect(id='n_search_in_category') and add the required data to dmc.MultiSelect(id='n_search_in_types').

In version 0.14.4 this stopped working, because now when dmc.MultiSelect(id='n_search_in_types') is updated (update data, not user values) - the selected parameters are reset. Moreover, even without updating this component and selecting some options in this component - the data is reset. Moreover, this does not happen in the component with categories.

siddharthajuprod07 commented 2 months ago

I am also facing similar issues @snehilvj @AnnMarieW

tepelbaum commented 2 months ago

Same issue for me as well :(. Has a fix been found?

celia-lm commented 2 months ago

It took me a while to understand what this issue was exactly, so here's a short recording of the behaviour with 0.14.3 vs 0.14.4 and the code to run this MRE:

dmc==0.14.3: when the options (data) of dmc.MultiSelect are updated, the selected values (value) are NOT modified, even if some of the options are not available anymore as part of data.

https://github.com/user-attachments/assets/6af42867-0c9c-45f3-8ee7-dd90a5be87d4

dmc==0.14.4: when the options (data) of dmc.MultiSelect are updated, the selected values (value) are CLEARED, even if some of the options are still available as part of data.

https://github.com/user-attachments/assets/d1e85a6c-a694-481d-ac19-3c6ccf03764b

Expected behaviour: when the options (data) of dmc.MultiSelect are updated, the (now) unavailable options are removed from value and the (still) available options are kept.

MRE


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

# dash==2.17.1
print(dmc.__version__)
dash._dash_renderer._set_react_version('18.2.0')
app = dash.Dash(__name__)

all_types = {
    L:[f"{L}{i}" for i in range(5)] for L in ["A", "B", "C", "D"]
}

app.layout = dmc.MantineProvider(dmc.Container([
    dmc.MultiSelect(
        w="47%",
        searchable=True,
        hidePickedOptions=True,
        clearable=True,
        label="Select Category",
        id="n_search_in_category",
        data=["A", "B", "C", "D"],
        value=[]),
    dmc.MultiSelect(
        w="47%",
        searchable=True,
        hidePickedOptions=True,
        clearable=True,
        label="Types to search",
        id="n_search_in_types",
        placeholder="Search all types",
        disabled=True,
        value=[])
]))

@callback(
    Output("n_search_in_types", "data"),
    Output("n_search_in_types", "disabled"),
    Output("n_search_in_category", "placeholder"),
    Output("n_search_in_types", "placeholder"),
    Input("n_search_in_category", "value"),
    State("n_search_in_types", "value"), # or Input, doesn't matter
)
def add_types_in_search(category_id, selected_types):
    # some work code
    types_select_data = [
        {"group":L, "items":all_types[L]}
        for L in category_id
    ]
    return (
            types_select_data, # dict with values
            False,
            "Search by categories",
            (
                "Search by selected types"
                if len(selected_types) > 0
                else "Search by all types"
            ),
        )

app.run(debug=True)
celia-lm commented 2 months ago

It's related to this (closed) issue: https://github.com/snehilvj/dash-mantine-components/issues/201 For some reason, it doesn't seem to keep the valid selected values.

MichaelODeli commented 2 months ago

But... Why? If I'm changing possible selections on the server side - why erase something that has already been entered by the user? If what the user entered is erased, that's correct, but why reset it completely? This greatly affects the principles of user-friendly interface.

celia-lm commented 2 months ago

Based on @snehilvj message in the issue I linked, it's not intentional:

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

It looks like the bug is present with grouped options, but not when the options are a list of strings.

types_select_data = sum([all_types[L] for L in category_id], []) # output: list of strings instead of list of dicst

https://github.com/user-attachments/assets/cddb3e1c-87e5-476e-b0c3-57d08a6df946