-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better tooltips for charts; ability to change formats for numbers and dates (WIP) #2468
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
import { each, debounce, isArray, isObject } from 'underscore'; | ||
import $ from 'jquery'; | ||
|
||
import Plotly from 'plotly.js/lib/core'; | ||
import bar from 'plotly.js/lib/bar'; | ||
import pie from 'plotly.js/lib/pie'; | ||
import histogram from 'plotly.js/lib/histogram'; | ||
import box from 'plotly.js/lib/box'; | ||
|
||
import './plotly.less'; | ||
|
||
import { | ||
ColorPalette, | ||
prepareData, | ||
|
@@ -14,14 +17,17 @@ import { | |
updateDimensions, | ||
updateData, | ||
normalizeValue, | ||
prepareTooltipPoints, | ||
calculateTooltipPosition, | ||
renderTooltipContents, | ||
} from './utils'; | ||
|
||
Plotly.register([bar, pie, histogram, box]); | ||
Plotly.setPlotConfig({ | ||
modeBarButtonsToRemove: ['sendDataToCloud'], | ||
modeBarButtonsToRemove: ['sendDataToCloud', 'hoverClosestCartesian', 'hoverCompareCartesian', 'toggleSpikelines'], | ||
}); | ||
|
||
const PlotlyChart = () => ({ | ||
const PlotlyChart = $sanitize => ({ | ||
restrict: 'E', | ||
template: '<div class="plotly-chart-container" resize-event="handleResize()"></div>', | ||
scope: { | ||
|
@@ -34,6 +40,10 @@ const PlotlyChart = () => ({ | |
let layout = {}; | ||
let data = []; | ||
|
||
const tooltip = $('<div>') | ||
.addClass('plotly-chart-tooltip') | ||
.appendTo('body'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we render our own tooltip... I can see how this can become a maintenance nightmare, chasing various Plotly edge cases 🤔 What do we lose if we go with Plotly's builtin tooltip? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried a lot of settings, and noticed only two edge cases: bar charts with stacking enabled, and pie charts. for both we just need to compute position for tooltip in a different way (for pie we already receive position in event data). Own tooltip allows to show all data in a single place (like in Highcharts) - x-axis value, all y values, etc. With default tooltip, we can customize text for y-values but not for x, and (if you remember) Plotly shows tooltip for each series for the same If you're afraid that it may be a nightmare - of course, I can simplify it, but we will lose some formatting and styling. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Past experience shows that there are always unexpected scenarios with charts :) Once we merge and deploy this, we're committed to it. But I don't think we need this distraction right now (having to maintain and fix issues related to the tooltip) -- we have more interesting stuff to do. So let's shelf this for now, and implement something simpler:
While it's not as complete as what you implemented here, it will serve most of the cases. |
||
|
||
const updateChartDimensions = () => { | ||
if (updateDimensions(layout, plotlyElement, calculateMargins(plotlyElement))) { | ||
Plotly.relayout(plotlyElement, layout); | ||
|
@@ -64,6 +74,36 @@ const PlotlyChart = () => ({ | |
}); | ||
|
||
plotlyElement.on('plotly_afterplot', updateChartDimensions); | ||
|
||
plotlyElement.on('plotly_hover', (hoverData) => { | ||
const points = prepareTooltipPoints(hoverData.points, data); | ||
if (points.length > 0) { | ||
const bounds = plotlyElement.getBoundingClientRect(); | ||
const offsetLeft = bounds.left + window.scrollX; | ||
const offsetTop = bounds.top + window.scrollY; | ||
|
||
|
||
const { left, top } = calculateTooltipPosition(points, scope.options); | ||
tooltip | ||
.css({ | ||
left: Math.round(left + offsetLeft) + 'px', | ||
top: Math.round(top + offsetTop) + 'px', | ||
}) | ||
.html($sanitize(renderTooltipContents(points, data, scope.options))) | ||
.show(); | ||
|
||
const tooltipBounds = tooltip[0].getBoundingClientRect(); | ||
if (tooltipBounds.top < 0) { | ||
tooltip | ||
.css({ | ||
top: Math.round(top + offsetTop - tooltipBounds.top) + 'px', | ||
}); | ||
} | ||
} | ||
}); | ||
plotlyElement.on('plotly_unhover', () => { | ||
tooltip.hide(); | ||
}); | ||
} | ||
update(); | ||
|
||
|
@@ -79,6 +119,10 @@ const PlotlyChart = () => ({ | |
}, true); | ||
|
||
scope.handleResize = debounce(updateChartDimensions, 50); | ||
|
||
scope.$on('$destroy', () => { | ||
tooltip.remove(); | ||
}); | ||
}, | ||
}); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
.plotly-chart-tooltip { | ||
pointer-events: none; | ||
display: none; | ||
position: absolute; | ||
z-index: 999999; | ||
transform: translate(-50%, -100%); | ||
margin: -10px 0 0 0; | ||
|
||
background: #fff; | ||
border: 1px solid #ccc; | ||
color: #333; | ||
left: 100px; | ||
top: 100px; | ||
padding: 10px 15px; | ||
border-radius: 5px; | ||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1); | ||
|
||
&:after { | ||
content: ''; | ||
font-size: 1px; | ||
display: block; | ||
width: 10px; | ||
height: 10px; | ||
position: absolute; | ||
left: 50%; | ||
bottom: 0; | ||
border: inherit; | ||
border-top: 0; | ||
border-left: 0; | ||
background: inherit; | ||
transform-origin: 50% 50%; | ||
transform: translate(-50%, 50%) rotate(45deg); | ||
} | ||
|
||
> div { | ||
white-space: nowrap; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default, for formats like
0[.]00%
numeral
will multiply value by 100 - so0.5
will become50%
. This option disables this behavior so0.5
will become0.5%
, and50
-50%
.