Repository navigation
fix: keep time chart series colors stable across refreshes - #3646
willie-engelbrecht wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: 39f89a1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@willie-engelbrecht is attempting to deploy a commit to the HyperDX Team on Vercel. A member of the Team first needs to authorize it. |
|
Hi @willie-engelbrecht, thanks for the pull request! Before we review code from a first-time contributor, a maintainer vouches for them, and you're not on our list yet. There's nothing else to file and this PR stays open — a maintainer will look at it, usually within a day or two, and vouch for you right here if it's a genuine attempt at the problem. To make that quick, check the PR description has:
More detail in our contributing guide. |
| // Series are otherwise ordered by first appearance in the response, which | ||
| // ClickHouse does not keep stable between runs. Colors are assigned by | ||
| // position, so tie-break by key to keep each series' color stable. | ||
| let sortedLineData = Object.values(lineDataMap).sort((a, b) => { |
There was a problem hiding this comment.
🟠 major — Colours still depend on position, so they shift whenever the set of series changes
Sorting fixes colours only when the same series come back in a different order. setLineColors (ChartUtils.tsx:637) still hands out colorIndex++ by position, and the set of series often changes when the user moves the time window, which is the repro in the PR description. Example: refresh 1 returns cart, checkout, email, so checkout gets COLORS[1]. Refresh 2 has no rows for cart, so checkout moves to COLORS[0] and email to COLORS[1]. The maxSeries cap causes the same shift when the kept top-N changes. To make a series' colour independent of what else is in the result, pick the palette index from the series key, e.g. hashCode(currentPeriodKey) from @hyperdx/common-utils/dist/core/utils modulo the palette size. Add a test where one series is missing from the second response.
Nothing blocks merge automatically, but a maintainer will expect this fixed if it is a real defect in code this PR changes. If it is about surrounding code, reply and say so instead of patching. Do not widen the PR. How to respond
| logLevelColorOrder.findIndex(color => color === a.color) - | ||
| logLevelColorOrder.findIndex(color => color === b.color) | ||
| logLevelColorOrder.findIndex(color => color === b.color) || | ||
| (a.currentPeriodKey < b.currentPeriodKey |
There was a problem hiding this comment.
🔵 minor — Comparing strings with < puts numeric group values out of order
currentPeriodKey comparison is by UTF-16 code unit, so numeric group-bys (status codes, partition ids, durations) come out as '10', '100', '9', and uppercase names sort before lowercase. The changeset says series are now 'ordered by name', and this comparison decides the series order in the chart, so the user sees this ordering. Use a.currentPeriodKey.localeCompare(b.currentPeriodKey, undefined, { numeric: true }), or an Intl.Collator with numeric: true created once outside the comparator.
Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond
PR Review
3 finding(s): 🔴 0 critical · 🟠 1 major · 🔵 2 minor 2 posted as inline comment(s) on the changed lines. 1 listed below. Findings outside the changed lines1 minor
Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides. |
Deep ReviewScope: ✅ No critical issues found. The comparator is correct: the 🟡 P2 — recommended
🔵 P3 nitpicks (2)
Reviewers (6): correctness, testing, maintainability, project-standards, kieran-typescript, performance. Testing gaps: New color-stability test covers only row-reorder of a fixed 3-series non-log set; no coverage for the cap-trips path or for log-level-colored series under reordered rows (the latter is partially locked by the updated static log-level expectation). No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond |
Summary
Line colours on panels are assigned by series position, and ClickHouse returns groups in a different order as the time window moves, based on which blocks were processed at that particular point in time. This PR resolves by sorting the series alphabetically first, and then assigning the colour based on the now sorted alphabetical sorted list. Refreshing a graph now keeps the series colours consistent.
Screenshots
Before: Here you can see the colours assigned to each series. When reloading a page, or updating the time interval to a new internal, the series colours are assigned at somewhat random order:

After a second refresh:

And another refresh:

After: Now the colours assigned to each series remains the same, no matter how many times you refresh the page or update the time picker.

How to reproduce
Before:
Open a graph with multiple lines, and change the time picker to "last 15 minutes". Watch the colours selected on the graph. Keep repeating this process and you'll see after a few refreshes that the colours are seemingly assigned in random order to the same series. This is because ClickHouse does not guarantee the order of the returned series.
After: Do the same as above, but now the series colours remain the same no matter how many times you refresh. This creates a richer user experience as a user might expect the graph to remain the same colours as they are used.