Skip to content

fix multiselect dropdown with components as labels#3643

Open
AnnMarieW wants to merge 5 commits intoplotly:devfrom
AnnMarieW:fix-multi-select-dropdown-with-components-as-labels
Open

fix multiselect dropdown with components as labels#3643
AnnMarieW wants to merge 5 commits intoplotly:devfrom
AnnMarieW:fix-multi-select-dropdown-with-components-as-labels

Conversation

@AnnMarieW
Copy link
Collaborator

closes #3642

This PR fixes the error in the second image below. In the sample app, it initially renders correctly, But after changing the selections it does not render correctly (in this case the yellow is deselected).

image

Updated:

image
from dash import Dash, dcc, html

app = Dash()
app.layout = html.Div([
    dcc.Dropdown(
        options=[
            {
                "label":html.Span("red"),
                "value": "red",
            },
            {
                "label":  html.Span("yellow"),
                "value": "yellow",
            },
            {
                "label":html.Span("blue"),
                "value": "blue",
            },
        ],
        value=['red','yellow','blue'],
        id='components-label-dropdown', multi=True
    ),
])


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

@AnnMarieW
Copy link
Collaborator Author

Hi @KoolADE85
This fix break the feature of keeping the focus in the options after a selection. This test now fails: tests/integration/dropdown/test_a11y.py:205 test_a11y006_multi_select_keyboard_focus_retention

Any ideas how to fix it?

setDisplayOptions(sortedOptions);
}
}, [filteredOptions, isOpen]);
}, [filteredOptions, isOpen, sanitizedValues]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this to the dependency array does appear to fix the bug, but I think we can fix it at a deeper level and avoid the side effects that are seen in the failing tests.

I believe the root bug is actually in optionRendering.tsx where we render the component via an ExternalWrapper.

Here, the componentPath (required for the dash renderer) is constructed using the option's array index:

componentPath={[...ctx.componentPath, index, i]}

I believe this is the problem. When we select an option, its index changes (it moves to the top of the list) but we don't have a way to inform the renderer. So, internally, it becomes confused about which component is at what index. It's confused until you re-open the dropdown or force a re-render of everything, which is what your original fix did.

We can instead construct a componentPath using the option's value (which should be unique across options):

componentPath={[...ctx.componentPath, String(value), i]}

This will allow the renderer to keep a stable path to use internally and the index won't matter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! That works, and this solution has better performance.

I did have to change the test I added because the options do not change position while the dropdown is open, but I think that's a better UX

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Multi-select dropdown options do not update correctly when labels have HTML component in dash 4.0.0

2 participants