[BUG] dcc.Clipboard functionality broken in Notification component
DavidKatz-il opened this issue · comments
The following code creates a notification using dash_mantine_components.Notification
, with a dcc.Clipboard
as its icon.
Since dash
version 2.15.0
, the clipboard functionality appears to be broken.
It seems that this issue is related to the following PR #2652.
from dash import Dash, html, callback, Output, Input, dcc, dash
import dash_mantine_components as dmc
@callback(
Output("notifications-container", "children"),
Input("notify", "n_clicks"),
prevent_initial_call=True,
)
def notify(n_clicks):
return dmc.Notification(
title="Notification",
autoClose=False,
id="simple-notify",
action="show",
message=f"{n_clicks=}",
icon=dcc.Clipboard(title="copy", content="icon text"),
)
app = Dash(__name__)
app.layout = dmc.NotificationsProvider(
html.Div(
[
dmc.Text(children=dash.__version__),
dmc.Button("Show Notification", id="notify"),
html.Div(id="notifications-container"),
html.Div(id="container"),
]
)
)
if __name__ == "__main__":
app.run(debug=True)
pip list | grep dash
Works with:
dash 2.14.0
dash-core-components 2.0.0
dash-html-components 2.0.0
dash-iconify 0.1.2
dash-mantine-components 0.12.1
dash-pivottable 0.0.2
dash-table 5.0.0
dash-testing-stub 0.0.2
Doesn't work with:
dash 2.15.0
dash-core-components 2.0.0
dash-html-components 2.0.0
dash-iconify 0.1.2
dash-mantine-components 0.12.1
dash-pivottable 0.0.2
dash-table 5.0.0
dash-testing-stub 0.0.2
Is this issue related to dash
or to dash_mantine_components
?
Thanks for reporting. I could replicate this with your example. Given that it's using the same version of dmc, it's reasonable to assume that it's caused by something that changed between Dash versions 2.14 and 2.15.
I'm not sure what is causing the issue though. To narrow things down, I checked to see if it was because of dcc.Clipboard
being returned in a callback or used to set a prop. This works in both Dash versions -- the callback returns a a dcc.Checklist
rather than the dmc.Notification
:
@callback(
Output("notifications-container", "children"),
Input("notify", "n_clicks"),
prevent_initial_call=True,
)
def notify(n_clicks):
return dcc.Checklist(
[
{
"label": [
html.Span(dcc.Clipboard(title="copy", content="icon text", id="id1")),
html.Span("Python", style={"font-size": 15, "padding-left": 10}),
],
"value": "Python",
},
], labelStyle={"display": "flex", "align-items": "center"}
)
Not sure what in the dmc.Notification
is conflicting. Do you have any ideas?
Hi @AnnMarieW, Thanks for the quick response.
I noticed that it works fine with Dash callbacks.
Upon debugging in the browser, I found that in the case of dmc.Notification
, this.props.n_clicks
always remains 0. Consequently, it triggers this code block:
if (
!this.props.n_clicks ||
this.props.n_clicks === prevProps.n_clicks
) {
return;
}
This behavior seems to have been added in PR #2652.
That’s helpful - thanks @DavidKatz-il !
The question is why is it not registering the n_clicks
when it's in the dmc.Notification
. The n_clicks
works fine when it's returned in the dcc.Checklist
🤔
Here's another minimal app to narrow down the issue. In this one, I've replaced the dcc.Clipboard
with an html.Button
If you click on the button in the notification, you will see that the callback is triggered but n_clicks
remains on 1 even after clicking on the button multiple times. Given that n_clicks
doesn't change then the new code in dcc.Clipboard
as mentioned above makes it so the clipboard doesn't work.
So maybe it's a dmc issue? It might be due to it being in the icon
prop.
from dash import Dash, html, callback, Output, Input, dcc, dash
import dash_mantine_components as dmc
app = Dash(__name__, suppress_callback_exceptions=True)
app.layout = dmc.NotificationsProvider(
html.Div(
[
dmc.Text(children=dash.__version__),
dmc.Button("Show Notification", id="notify"),
html.Div(id="notifications-container"),
html.Div(id="container"),
]
),
)
@callback(
Output("notifications-container", "children"),
Input("notify", "n_clicks"),
prevent_initial_call=True,
)
def notify(n_clicks):
return dmc.Notification(
title="Notification",
autoClose=False,
id="simple-notify",
action="show",
message=f"{n_clicks=}",
icon=html.Button("click me", id="button"),
)
@callback(
Output("container", "children"),
Input("button", "n_clicks")
)
def update(n):
print("button n_clicks:", n)
return dash.no_update
if __name__ == "__main__":
app.run(debug=True)
Additionally, the same issue occurred with the message
prop.
from dash import Dash, html, callback, Output, Input, dcc, dash
import dash_mantine_components as dmc
@callback(
Output("notifications-container", "children"),
Input("notify", "n_clicks"),
prevent_initial_call=True,
)
def notify(n_clicks):
return dmc.Notification(
title="Notification",
autoClose=False,
id="simple-notify",
action="show",
message=dcc.Clipboard(title="copy", content="message text"),
)
app = Dash(__name__)
app.layout = dmc.NotificationsProvider(
html.Div(
[
dmc.Text(children=dash.__version__),
dmc.Button("Show Notification", id="notify"),
html.Div(id="notifications-container"),
html.Div(id="container"),
]
)
)
if __name__ == "__main__":
app.run(debug=True)
So maybe it's a dmc issue?
Interesting, dmc must be doing something with the props and never receive the new n_clicks value in the onClick handler.