Skip to content

Return actual data values (rather than calcdata values) for xvals / yvals in hoveranywhere and clickanywhere events - #7964

Open
emilykl wants to merge 3 commits into
v4.0from
hover-click-anywhere-c2d-2
Open

Return actual data values (rather than calcdata values) for xvals / yvals in hoveranywhere and clickanywhere events#7964
emilykl wants to merge 3 commits into
v4.0from
hover-click-anywhere-c2d-2

Conversation

@emilykl

@emilykl emilykl commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Closes #7816

Modify hoverdata and clickdata such that the xvals / yvals properties always contain data values (in the same form as the input data, i.e. may be strings in some cases) rather than calcdata values (which are always numeric).

Result:

  • For ordinary numeric axes (e.g. linear and log) this change has (essentially) no effect.
    • Technically there is some validation that happens during c2d, but it's basically just checking that the values are numeric, and returning undefined if not
  • For date axes, xvals / yvals now contain date strings rather than numbers representing milliseconds. (This addresses the bug reported in [BUG]: hoveranywhere / clickanywhere return incorrect timestamps for date axes with local time zones #7816)
  • For category axes, xvals / yvals now contain strings corresponding to category names rather than numeric values.

The attribute descriptions for hoveranywhere / clickanywhere refer to xvals and yvals being "in data space", so I believe this was always the intended behavior.

Steps for testing

  • Check out this branch
  • Create the following HTML file in the repo root. This HTML page creates a plotly.js plot where the x-axis is a date axis and the y-axis is a category axis:
Details
<!doctype html>
<meta charset="utf-8">
<title>hoveranywhere / clickanywhere coordinates</title>
<script src="dist/plotly.js"></script>
<p>Open the console, then hover and click anywhere in the plot.</p>
<div id="gd" style="width:700px;height:400px"></div>
<script>
    var gd = document.getElementById('gd');

    // local midnight -> next local midnight, as in issue #7816
    var dayStart = new Date(2026, 4, 31);
    var dayEnd = new Date(2026, 5, 1);

    Plotly.newPlot(gd, [{
        x: [new Date(2026, 4, 31, 6), new Date(2026, 4, 31, 18)],
        y: ['b', 'c'],
        mode: 'markers',
        marker: {size: 12}
    }], {
        xaxis: {type: 'date', range: [dayStart, dayEnd]},
        yaxis: {type: 'category', categoryorder: 'array', categoryarray: ['a', 'b', 'c', 'd']},
        hoveranywhere: true,
        clickanywhere: true,
        margin: {t: 20}
    });

    function log(name) {
        return function(d) {
            console.log(name, {
                xvals: d.xvals[0],
                yvals: d.yvals[0],
                'points[0].x': d.points[0] && d.points[0].x,
                'points[0].y': d.points[0] && d.points[0].y
            });
        };
    }

    gd.on('plotly_hover', log('hover'));
    gd.on('plotly_click', log('click'));
</script>
  • Run npm ci && npm run build
  • Open the HTML file in the browser, hover anywhere in the plot, and look at the console output. Note that xvals contains date strings and yvals contains a string matching a category (correct behavior)
  • Check out v4.0 and run npm ci && npm run build
  • Refresh the browser and note that xvals and yvals are now both numeric (incorrect behavior)

@emilykl emilykl changed the title Hover click anywhere c2d 2 Return actual data values (rather than calcdata values) for xvals / yvals in hoveranywhere and clickanywhere events Aug 13, 2026
@emilykl
emilykl requested a review from camdecoster August 13, 2026 22:54

@camdecoster camdecoster left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good and handles the issue well. I made a few comments.

// the 300px-wide plot area spans exactly one day, so 0px is local midnight
// and 150px is local noon, in any timezone
function makeDatePlot(gd, layoutExtras) {
return makePlot(gd, Lib.extendFlat({ xaxis: { type: 'date', range: [dayStart, dayEnd] } }, layoutExtras), {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should arguments 2 and 3 be swapped? Or should layoutExtras be renamed to traceExtras?

Comment thread draftlogs/7964_change.md
@@ -0,0 +1 @@
- Return actual data values (rather than calcdata values) for `xvals` / `yvals` in `hoveranywhere` and `clickanywhere` events [[#7964](https://github.com/plotly/plotly.js/pull/7964)]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Maybe throw a breaking change notice in here?

Suggested change
- Return actual data values (rather than calcdata values) for `xvals` / `yvals` in `hoveranywhere` and `clickanywhere` events [[#7964](https://github.com/plotly/plotly.js/pull/7964)]
- **Breaking**: Return actual data values (rather than calcdata values) for `xvals` / `yvals` in `hoveranywhere` and `clickanywhere` events [[#7964](https://github.com/plotly/plotly.js/pull/7964)]

@camdecoster

Copy link
Copy Markdown
Contributor

Could you also take look at updating the hand written TS types per this change?

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.

2 participants