From 086dec897020bdee8426f7f4ed4b41e7ab43270e Mon Sep 17 00:00:00 2001 From: AnnMarueW Date: Mon, 29 Jun 2026 07:50:47 -0700 Subject: [PATCH 1/4] include xvals and yvals when hoveranywhere or clickanywhere is enabled --- .../src/fragments/Graph.react.js | 16 ++++++ .../integration/graph/test_graph_basics.py | 50 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/components/dash-core-components/src/fragments/Graph.react.js b/components/dash-core-components/src/fragments/Graph.react.js index 14edaaba61..ef539f1b35 100644 --- a/components/dash-core-components/src/fragments/Graph.react.js +++ b/components/dash-core-components/src/fragments/Graph.react.js @@ -119,6 +119,22 @@ const filterEventData = (gd, eventData, event) => { points[i] = pointData; } filteredEventData = {points}; + + const includeXYVals = + (event === 'hover' && + gd._fullLayout.hoveranywhere === true) || + (event === 'click' && + gd._fullLayout.clickanywhere === true); + + if (includeXYVals && has('xvals', eventData)) { + filteredEventData.xvals = eventData.xvals; + } + + if (includeXYVals && has('yvals', eventData)) { + filteredEventData.yvals = eventData.yvals; + } + + } else if (event === 'relayout' || event === 'restyle') { /* * relayout shouldn't include any big objects diff --git a/components/dash-core-components/tests/integration/graph/test_graph_basics.py b/components/dash-core-components/tests/integration/graph/test_graph_basics.py index 94a871e922..ca6357be22 100644 --- a/components/dash-core-components/tests/integration/graph/test_graph_basics.py +++ b/components/dash-core-components/tests/integration/graph/test_graph_basics.py @@ -547,3 +547,53 @@ def show_figure_state(figure): dash_dcc.wait_for_text_to_equal("#output", "color=green, title=Updated Title 3") assert dash_dcc.get_logs() == [] + + + +def test_grbs011_clickanywhere_hoveranywhere(dash_dcc): + """When clickanywhere and hoveranywhere are enabled, clickData and hoverData include xvals and yvals""" + app = Dash(__name__) + + app.layout = html.Div( + [ + dcc.Graph( + id="graph", + figure=go.Figure( + layout=go.Layout(hoveranywhere=True, clickanywhere=True) + ), + style={"width": 600, "height": 400}, + ), + html.Div(id="output", children="[]"), + ] + ) + + @app.callback( + Output("output", "children"), + Input("graph", "clickData"), + Input("graph", "hoverData"), + prevent_initial_call=True, + ) + def on_event(click_data, hover_data): + result = { + "click_x": (click_data or {}).get("xvals", [None])[0], + "click_y": (click_data or {}).get("yvals", [None])[0], + "hover_x": (hover_data or {}).get("xvals", [None])[0], + "hover_y": (hover_data or {}).get("yvals", [None])[0], + } + return json.dumps(result) + + dash_dcc.start_server(app) + dash_dcc.wait_for_element("#graph .main-svg") + + graph = dash_dcc.find_element("#graph .nsewdrag") + graph.click() + + dash_dcc.wait_for_contains_text("#output", "{") + + out = json.loads(dash_dcc.find_element("#output").text) + + # click anywhere should produce coordinates + assert out["click_x"] is not None + assert out["click_y"] is not None + assert out["hover_x"] is not None + assert out["hover_y"] is not None From 5931c51d6939f4624ea44158f83e0c421dfce4f6 Mon Sep 17 00:00:00 2001 From: AnnMarueW Date: Mon, 29 Jun 2026 08:56:07 -0700 Subject: [PATCH 2/4] lint --- .../dash-core-components/src/fragments/Graph.react.js | 8 ++------ .../tests/integration/graph/test_graph_basics.py | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/components/dash-core-components/src/fragments/Graph.react.js b/components/dash-core-components/src/fragments/Graph.react.js index ef539f1b35..1ca8a00510 100644 --- a/components/dash-core-components/src/fragments/Graph.react.js +++ b/components/dash-core-components/src/fragments/Graph.react.js @@ -121,10 +121,8 @@ const filterEventData = (gd, eventData, event) => { filteredEventData = {points}; const includeXYVals = - (event === 'hover' && - gd._fullLayout.hoveranywhere === true) || - (event === 'click' && - gd._fullLayout.clickanywhere === true); + (event === 'hover' && gd._fullLayout.hoveranywhere === true) || + (event === 'click' && gd._fullLayout.clickanywhere === true); if (includeXYVals && has('xvals', eventData)) { filteredEventData.xvals = eventData.xvals; @@ -133,8 +131,6 @@ const filterEventData = (gd, eventData, event) => { if (includeXYVals && has('yvals', eventData)) { filteredEventData.yvals = eventData.yvals; } - - } else if (event === 'relayout' || event === 'restyle') { /* * relayout shouldn't include any big objects diff --git a/components/dash-core-components/tests/integration/graph/test_graph_basics.py b/components/dash-core-components/tests/integration/graph/test_graph_basics.py index ca6357be22..8ed8f3b328 100644 --- a/components/dash-core-components/tests/integration/graph/test_graph_basics.py +++ b/components/dash-core-components/tests/integration/graph/test_graph_basics.py @@ -549,7 +549,6 @@ def show_figure_state(figure): assert dash_dcc.get_logs() == [] - def test_grbs011_clickanywhere_hoveranywhere(dash_dcc): """When clickanywhere and hoveranywhere are enabled, clickData and hoverData include xvals and yvals""" app = Dash(__name__) From 3b1e8638e02ebdd153b4fc5698de57b8c8165e6c Mon Sep 17 00:00:00 2001 From: AnnMarueW Date: Mon, 29 Jun 2026 10:08:54 -0700 Subject: [PATCH 3/4] added changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f0d7c9abc..cb7ac45cba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## Added - [#3796](https://github.com/plotly/dash/pull/3796) MCP: Add `configure_mcp_server()` to toggle which content the MCP server exposes (`include_layout`, `include_callbacks`, `include_clientside_callbacks`, `include_pages`, `expose_callback_docstrings`). Only the parameters explicitly passed are updated; omitted parameters retain their current value. +- [#3852](https://github.com/plotly/dash/pull/3852) Added support for Plotly hoveranywhere and clickanywhere events in `dcc.Graph` by adding `xvals` and `yvals` to `hoverData` and `clickData`. ## Changed - [#3796](https://github.com/plotly/dash/pull/3796) MCP: Remove the `mcp_expose_docstrings` `Dash()` constructor argument; callback docstring exposure is now controlled via `configure_mcp_server(expose_callback_docstrings=...)`. From b8dcfd9ea84a8fdd4c307a2e421f52fd83e932b9 Mon Sep 17 00:00:00 2001 From: AnnMarueW Date: Mon, 29 Jun 2026 15:21:26 -0700 Subject: [PATCH 4/4] added axes_id --- .../src/fragments/Graph.react.js | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/components/dash-core-components/src/fragments/Graph.react.js b/components/dash-core-components/src/fragments/Graph.react.js index 1ca8a00510..a9c75c4df0 100644 --- a/components/dash-core-components/src/fragments/Graph.react.js +++ b/components/dash-core-components/src/fragments/Graph.react.js @@ -124,12 +124,22 @@ const filterEventData = (gd, eventData, event) => { (event === 'hover' && gd._fullLayout.hoveranywhere === true) || (event === 'click' && gd._fullLayout.clickanywhere === true); - if (includeXYVals && has('xvals', eventData)) { - filteredEventData.xvals = eventData.xvals; - } + if (includeXYVals) { + if (has('xvals', eventData)) { + filteredEventData.xvals = eventData.xvals; + } + + if (has('yvals', eventData)) { + filteredEventData.yvals = eventData.yvals; + } - if (includeXYVals && has('yvals', eventData)) { - filteredEventData.yvals = eventData.yvals; + if (has('xaxes', eventData)) { + filteredEventData.xaxes_id = eventData.xaxes[0]._id; + } + + if (has('yaxes', eventData)) { + filteredEventData.yaxes_id = eventData.yaxes[0]._id; + } } } else if (event === 'relayout' || event === 'restyle') { /*