Skip to content

Commit e9fb8e3

Browse files
authored
Query fixes: stale widget fix, multiple series colors mismatch (#3126)
- Fix for series color assignment being out of sync with the graph (ensures added series colors match their graph representation) - Reload widgets when returning to screen if props changed (prevents stale widgets after filtering and scrolling)
1 parent c05b30a commit e9fb8e3

File tree

3 files changed

+17
-5
lines changed

3 files changed

+17
-5
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Fix metrics dashboard chart series colors going out of sync and widgets not reloading stale data when scrolled back into view

apps/webapp/app/components/code/QueryResultsChart.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -889,13 +889,15 @@ export const QueryResultsChart = memo(function QueryResultsChart({
889889
const cfg: ChartConfig = {};
890890
sortedSeries.forEach((s, i) => {
891891
const statusColor = groupByIsRunStatus ? getRunStatusHexColor(s) : undefined;
892+
const originalIndex = config.yAxisColumns.indexOf(s);
893+
const colorIndex = originalIndex >= 0 ? originalIndex : i;
892894
cfg[s] = {
893895
label: s,
894-
color: statusColor ?? config.seriesColors?.[s] ?? getSeriesColor(i),
896+
color: statusColor ?? config.seriesColors?.[s] ?? getSeriesColor(colorIndex),
895897
};
896898
});
897899
return cfg;
898-
}, [sortedSeries, groupByIsRunStatus, config.seriesColors]);
900+
}, [sortedSeries, groupByIsRunStatus, config.seriesColors, config.yAxisColumns]);
899901

900902
// Custom tooltip label formatter for better date display
901903
const tooltipLabelFormatter = useMemo(() => {

apps/webapp/app/routes/resources.metric.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,19 @@ export function MetricWidget({
173173
const [response, setResponse] = useState<MetricWidgetActionResponse | null>(null);
174174
const [isLoading, setIsLoading] = useState(false);
175175
const abortControllerRef = useRef<AbortController | null>(null);
176+
const isDirtyRef = useRef(false);
176177

177178
// Track the latest props so the submit callback always uses fresh values
178179
// without needing to be recreated (which would cause useInterval to re-register listeners).
179180
const propsRef = useRef(props);
180181
propsRef.current = props;
181182

182183
const submit = useCallback(() => {
183-
// Skip fetching if the widget is not visible on screen
184-
if (!isVisibleRef.current) return;
184+
if (!isVisibleRef.current) {
185+
isDirtyRef.current = true;
186+
return;
187+
}
188+
isDirtyRef.current = false;
185189

186190
// Abort any in-flight request for this widget
187191
abortControllerRef.current?.abort();
@@ -225,7 +229,7 @@ export function MetricWidget({
225229
// When a widget scrolls into view and has no data yet, trigger a load.
226230
const { ref: visibilityRef, isVisibleRef } = useElementVisibility({
227231
onVisibilityChange: (visible) => {
228-
if (visible && !response) {
232+
if (visible && (!response || isDirtyRef.current)) {
229233
submit();
230234
}
231235
},

0 commit comments

Comments
 (0)