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
Table: Highlight row on shared crosshair #78392
Conversation
The |
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.
Nice! Take a look at the debounceTime
comment, after addressing that I think this is good to go. 🎉
subs.add( | ||
panelContext.eventBus | ||
.getStream(DataHoverEvent) | ||
.pipe(throttleTime(50)) |
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.
I would suggest replacing throttleTime
with debounceTime
and increasing the timeout to something like 250-500 ms.
throttleTime will take one event and ignore the rest for 50 milliseconds. debounceTime will ignore events until there hasn't been a new event in 50 milliseconds.
throttleTime has the effect that if a user drags his/her mouse over a bunch of points and then stops, it might not be the point the mouse is over when the users stops dragging.
debounceTime has the effect that if the user drags over a bunch of points they will be ignored until the mouse is stopped.
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.
Great idea! Will look into that, thank you!
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.
I've tested out the debounceTime
function but it adds a weird delay into the mix. My understanding of this func is that it only emits a value after a given time period passes without any new emits. So hovering over a point in a graph will result in a row highlight only after the timeout passes with no new events(e.g.: after half a second for debounceTime(500)
).
I would leave the throttle as it is to be in sync with how GraphNG
deals with DataHoverEvent
.
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.
I think the delay is something we would want here, the constant redrawing of the table is quite resource intense. Maybe lower the debounceTime from 500 to 100-200? Even if we just set it to 50 ms, debounceTime is going to highlight the correct point every time the mouse stops. With throttleTime, when the mouse stops on a point in the graph, the highlighted row is going to correspond to what was hovered 50ms ago, not what the mouse is currently hovering.
throttleTime might look better, but it's producing wrong results
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.
That makes sense, i agree. Switched to a debounceTime of 100.
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.
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.
i think we already throttle these eventbus events during emission. i think adding a second level of thottling wont be too good. maybe we can just increase the throttle time during emission?
export function calculateAroundPointThreshold(timeField: Field): number { | ||
let max = -Number.MAX_VALUE; | ||
let min = Number.MAX_VALUE; | ||
|
||
if (timeField.values.length < 2) { | ||
return 0; | ||
} | ||
|
||
for (let i = 0; i < timeField.values.length; i++) { | ||
const value = timeField.values[i]; | ||
if (value > max) { | ||
max = value; | ||
} | ||
if (value < min) { | ||
min = value; | ||
} | ||
} | ||
|
||
return (max - min) / timeField.values.length; | ||
} |
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.
This works a lot better! 🥇
@leeoniya I think memoing rows won't work because of the virtualised list. It only renders what is in the table view, so the index will keep updating as you scroll, or as the dataHoverEvent picks a new hover row and scrolls there. I've broken the row list part in a new component which rerenders on row hover but leaves the rest of the table alone, same as with a simple table scroll, so I think this should work performance-wise. There's still more stuff that could be moved from the parent component in here (e.g.: the actual RenderRow method), to better break the component. Overall, I think this could be a way forward. |
onRowHover={config.featureToggles.tableSharedCrosshair ? onRowHover : undefined} | ||
onRowLeave={config.featureToggles.tableSharedCrosshair ? onRowLeave : undefined} | ||
enableSharedCrosshair={config.featureToggles.tableSharedCrosshair && enableSharedCrosshair} | ||
eventBus={panelContext.eventBus} |
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.
I'm thinking that this can be also used in other places, potentially? Otherwise we could drop this prop and call the one in usePanelContext()
within RowsList
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.
I don't think ever should think of that , if visualizations are used outside a panel they need to be wrapped in PanelContext at least to access those features that use it
onRowHover?: (idx: number, frame: DataFrame) => void; | ||
onRowLeave?: () => void; | ||
/** Used to highlight a row at a given index */ | ||
rowHighlightIndex?: number; |
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.
This can be used standalone to highlight a row in some other scenarios. If set it overwrites the one received through the data hover event. Not sure if could be useful or if it's too much.
seeing some weird data flashing/changing in this test dashboard. is it trying to scroll the virtualized table to the row? but then stops working once it gets far enough in time? table-shared-crosshair-perf.json{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": 850,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 15,
"w": 22,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "10.3.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 7
}
],
"title": "Panel Title",
"transformations": [
{
"id": "joinByField",
"options": {}
}
],
"type": "table"
},
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 17,
"w": 22,
"x": 0,
"y": 15
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 1
}
],
"title": "Panel Title",
"type": "timeseries"
}
],
"refresh": "",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "table-shared-crosshair-perf",
"uid": "fa36a3f9-9b0f-49b7-97cf-f5f721bb5867",
"version": 3,
"weekStart": ""
} Peek.2023-11-28.13-44.mp4 |
subs.add( | ||
panelContext.eventBus | ||
.getStream(DataHoverClearEvent) | ||
.pipe(throttleTime(50)) |
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.
Seems like this should also be debounced
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.
Not sure about this per Leon's comment, but maybe?
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.
Yeah, I think this should be debounced! Thank you for pointing it out, will work on it! 🎉
@@ -533,3 +533,37 @@ export function getAlignmentFactor( | |||
return alignmentFactor; | |||
} | |||
} | |||
|
|||
export function hasTimeField(data: DataFrame): boolean { |
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.
I think this should probably be in @grafana/data
as we have functions like this there already
|
||
const timeField: Field = frame!.fields.find((f) => f.type === FieldType.time)!; | ||
|
||
panelContext.eventBus.publish( |
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.
It feels a bit complex to have logic split between internal Table component and the outer TablePanel.
Can't it all be on handled inside the table component, any benefit to having the subscribe there and the publish here ?
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.
I agree, there's no point in having this here. I must've overlooked this bit of code after the table breakdown. Will move it accordingly.
@leeoniya are you referring to the weird hover at the row in the middle of the view? It should scroll to the hovered row at all times, shouldn't stop when it gets far enough in time. Need to look more into that, might need to raise the debounce potentially. |
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 is this feature?
Adds shared crosshair support to the table panel. If the table has a time field, it will be able to highlight rows based on it and will also set the crosshair on the equivalent datapoint in other panels (e.g.: timeseries) on row hover.
Screen.Recording.2023-11-16.at.13.07.13.mov
Why do we need this feature?
Enhance table panel functionality.
Who is this feature for?
Everyone
Which issue(s) does this PR fix?:
Fixes #7310
Special notes for your reviewer:
TODO:
Please check that: