This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new 1c187f19d fix(metrics): keep mixed and pure series colors distinct 
(#4570)
1c187f19d is described below

commit 1c187f19d1b5ffa1e87c47d5eae78ca3685747dc
Author: 烤化の初雪 <[email protected]>
AuthorDate: Sat Sep 26 18:46:49 2026 +0800

    fix(metrics): keep mixed and pure series colors distinct (#4570)
    
    The slot arithmetic for split mixed-series lines could hand the same
    palette entry to a series's histogram line and the next pure series,
    leaving two indistinguishable polylines and legend swatches. Reassign
    duplicate strokes to the first free palette color in order.
---
 web/src/components/MetricsExplorer.tsx             | 13 ++++++++-
 .../components/__tests__/MetricsExplorer.test.tsx  | 34 ++++++++++++++++++++++
 2 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/web/src/components/MetricsExplorer.tsx 
b/web/src/components/MetricsExplorer.tsx
index b4c37865a..67027d8a2 100644
--- a/web/src/components/MetricsExplorer.tsx
+++ b/web/src/components/MetricsExplorer.tsx
@@ -148,7 +148,18 @@ const MetricChart = ({
       });
     })
     .filter((series) => series.samples.length > 0);
-
+  // The slot arithmetic above can hand the same palette entry to a split 
mixed series and
+  // the next pure series, which would make two polylines (and their legend 
swatches)
+  // indistinguishable. Reassign duplicates to the first free palette color in 
order.
+  const takenColors = new Set<string>();
+  for (const series of allSeries) {
+    if (!takenColors.has(series.color)) {
+      takenColors.add(series.color);
+      continue;
+    }
+    series.color = SERIES_COLORS.find((color) => !takenColors.has(color)) ?? 
series.color;
+    takenColors.add(series.color);
+  }
   if (allSeries.length === 0) {
     return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={noSamples} 
/>;
   }
diff --git a/web/src/components/__tests__/MetricsExplorer.test.tsx 
b/web/src/components/__tests__/MetricsExplorer.test.tsx
index c353f9c57..53b01fa7f 100644
--- a/web/src/components/__tests__/MetricsExplorer.test.tsx
+++ b/web/src/components/__tests__/MetricsExplorer.test.tsx
@@ -701,6 +701,40 @@ describe('MetricsExplorer', () => {
     expect(screen.getByText(/broker-a.*\(直方图\)/)).toBeInTheDocument();
   });
 
+  it('gives a mixed series and the following pure series distinct colors', 
async () => {
+    vi.mocked(queryMetrics).mockResolvedValue({
+      ...metricData,
+      series: [
+        {
+          ...metricData.series[0],
+          histograms: [
+            { timestamp: 1_800_000_001, histogram: { count: '99', sum: '999', 
buckets: [] } },
+          ],
+        },
+        {
+          labels: { cluster: 'prod', node_id: 'broker-b' },
+          values: [
+            { timestamp: 1_799_996_400, value: '30' },
+            { timestamp: 1_800_000_000, value: '31' },
+          ],
+          histograms: [],
+        },
+      ],
+    });
+
+    renderWithProviders(<MetricsExplorer />);
+
+    expect(await screen.findByText('42 messages/s')).toBeInTheDocument();
+    // The mixed broker-a line is split into a scalar and a histogram trend; 
the broker-b
+    // line follows. Every drawn polyline must keep its own stroke so legend 
swatches
+    // stay matchable.
+    const strokes = Array.from(
+      screen.getByRole('img', { name: 'Message In TPS time series' 
}).querySelectorAll('polyline'),
+    ).map((line) => line.getAttribute('stroke'));
+    expect(strokes.length).toBe(3);
+    expect(new Set(strokes).size).toBe(strokes.length);
+  });
+
   it('queries the selected data source through the datasource endpoint', async 
() => {
     const user = userEvent.setup();
     vi.mocked(listDataSources).mockResolvedValue([

Reply via email to