From 51dddcfde53b71daa57edf75e57c839068b180de Mon Sep 17 00:00:00 2001 From: Shurong Cao <170531907+CAOShurong@users.noreply.github.com> Date: Thu, 13 Aug 2026 02:24:33 +0800 Subject: [PATCH 1/6] Fix numeric color sorting in parcats bundles --- src/traces/parcats/parcats.js | 23 ++++++++++++++--------- test/jasmine/tests/parcats_test.js | 23 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/traces/parcats/parcats.js b/src/traces/parcats/parcats.js index 39f75d3b674..3015ff69475 100644 --- a/src/traces/parcats/parcats.js +++ b/src/traces/parcats/parcats.js @@ -370,6 +370,18 @@ function compareRawColor(a, b) { } } +function compareArrays(a, b) { + for(var i = 0; i < Math.min(a.length, b.length); i++) { + if(a[i] < b[i]) { + return -1; + } else if(a[i] > b[i]) { + return 1; + } + } + + return a.length - b.length; +} + /** * Handle path mouseover * @param {PathViewModel} d @@ -1734,15 +1746,8 @@ function updatePathViewModels(parcatsViewModel) { sortArray2.unshift(v2.rawColor); } - // colors equal, sort by display categories - if(sortArray1 < sortArray2) { - return -1; - } - if(sortArray1 > sortArray2) { - return 1; - } - - return 0; + // Sort by color, then display categories + return compareArrays(sortArray1, sortArray2); }); // Create path models diff --git a/test/jasmine/tests/parcats_test.js b/test/jasmine/tests/parcats_test.js index f6210c4521d..34b54fe42fa 100644 --- a/test/jasmine/tests/parcats_test.js +++ b/test/jasmine/tests/parcats_test.js @@ -284,6 +284,29 @@ describe('Basic parcats trace', function() { .then(done, done.fail); }); + it('should sort bundled paths by numeric color values', function(done) { + var trace = { + type: 'parcats', + dimensions: [ + {values: ['a', 'a', 'a', 'a']}, + {values: ['b', 'b', 'b', 'b']} + ], + line: {color: [1, 10, 2, 20]}, + bundlecolors: true + }; + + Plotly.newPlot(gd, [trace]) + .then(function() { + var parcatsViewModel = d3Select('g.trace.parcats').datum(); + var pathColors = parcatsViewModel.paths.map(function(path) { + return path.model.rawColor; + }); + + expect(pathColors).toEqual([1, 2, 10, 20]); + }) + .then(done, done.fail); + }); + it('should compute initial model views properly', function(done) { Plotly.newPlot(gd, basicMock) .then(function() { From 620bf41dc22c426c0eba7a82d843ba04415882b7 Mon Sep 17 00:00:00 2001 From: Shurong Cao <170531907+CAOShurong@users.noreply.github.com> Date: Thu, 13 Aug 2026 02:40:00 +0800 Subject: [PATCH 2/6] Add draftlog for #7959 --- draftlogs/7959_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7959_fix.md diff --git a/draftlogs/7959_fix.md b/draftlogs/7959_fix.md new file mode 100644 index 00000000000..4af4fbbf41c --- /dev/null +++ b/draftlogs/7959_fix.md @@ -0,0 +1 @@ +- Fix numeric color sorting for bundled parallel-categories paths [[#7959](https://github.com/plotly/plotly.js/pull/7959)] From aec4e47568bfaf7730c778382e14810c6497f306 Mon Sep 17 00:00:00 2001 From: Shurong Cao <170531907+CAOShurong@users.noreply.github.com> Date: Thu, 20 Aug 2026 02:25:50 +0800 Subject: [PATCH 3/6] Handle NaN values and add JSDoc to compareArrays Per review suggestion from camdecoster: - Add JSDoc comment documenting ascending-order comparison, NaN-sorting behavior, and prefix handling. - Handle values that do not order against each other (NaN, undefined) by sorting them after every orderable value. - Add test verifying NaN color values sort after orderable values in bundled parallel-categories paths. The 4 pre-existing drag/reorder test failures are unchanged by this commit (27->28 passing, same 4 baseline failures). --- src/traces/parcats/parcats.js | 23 +++++++++++++++++++---- test/jasmine/tests/parcats_test.js | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/traces/parcats/parcats.js b/src/traces/parcats/parcats.js index 3015ff69475..77e226b4032 100644 --- a/src/traces/parcats/parcats.js +++ b/src/traces/parcats/parcats.js @@ -370,12 +370,27 @@ function compareRawColor(a, b) { } } +/** + * Compare two sort arrays element by element in ascending order. + * Values that do not order against each other, for example NaN, sort last. + * The shorter array sorts first when one array is a prefix of the other. + * + * @param {Array} a + * @param {Array} b + */ function compareArrays(a, b) { for(var i = 0; i < Math.min(a.length, b.length); i++) { - if(a[i] < b[i]) { - return -1; - } else if(a[i] > b[i]) { - return 1; + var valA = a[i]; + var valB = b[i]; + + if(valA < valB) return -1; + if(valA > valB) return 1; + // Handle values that do not order against each other (NaN, undefined, etc.) + if(valA !== valB) { + // Sort these after every orderable value. + var badA = isNaN(valA); + var badB = isNaN(valB); + if(badA !== badB) return badA ? 1 : -1; } } diff --git a/test/jasmine/tests/parcats_test.js b/test/jasmine/tests/parcats_test.js index 34b54fe42fa..9686b5127a6 100644 --- a/test/jasmine/tests/parcats_test.js +++ b/test/jasmine/tests/parcats_test.js @@ -307,6 +307,32 @@ describe('Basic parcats trace', function() { .then(done, done.fail); }); + it('should sort NaN color values after orderable values', function(done) { + var trace = { + type: 'parcats', + dimensions: [ + {values: ['a', 'a', 'a', 'a']}, + {values: ['b', 'b', 'b', 'b']} + ], + line: {color: [10, NaN, 2, NaN]}, + bundlecolors: true + }; + + Plotly.newPlot(gd, [trace]) + .then(function() { + var parcatsViewModel = d3Select('g.trace.parcats').datum(); + var pathColors = parcatsViewModel.paths.map(function(path) { + return path.model.rawColor; + }); + + // Orderable values sort first, NaN values sort last + expect(pathColors.slice(0, 2)).toEqual([2, 10]); + expect(isNaN(pathColors[2])).toBe(true); + expect(isNaN(pathColors[3])).toBe(true); + }) + .then(done, done.fail); + }); + it('should compute initial model views properly', function(done) { Plotly.newPlot(gd, basicMock) .then(function() { From 1b12774ddd9c922b02b52d15b4a430808bf003af Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Wed, 19 Aug 2026 15:14:45 -0600 Subject: [PATCH 4/6] Update test to fix empty assertion --- test/jasmine/tests/parcats_test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/jasmine/tests/parcats_test.js b/test/jasmine/tests/parcats_test.js index 9686b5127a6..0aa41e83954 100644 --- a/test/jasmine/tests/parcats_test.js +++ b/test/jasmine/tests/parcats_test.js @@ -307,7 +307,7 @@ describe('Basic parcats trace', function() { .then(done, done.fail); }); - it('should sort NaN color values after orderable values', function(done) { + fit('should sort NaN color values after orderable values', function(done) { var trace = { type: 'parcats', dimensions: [ @@ -326,9 +326,9 @@ describe('Basic parcats trace', function() { }); // Orderable values sort first, NaN values sort last + expect(pathColors.length).toBe(3) expect(pathColors.slice(0, 2)).toEqual([2, 10]); expect(isNaN(pathColors[2])).toBe(true); - expect(isNaN(pathColors[3])).toBe(true); }) .then(done, done.fail); }); From d98a8c845dfaf3cfa91ecdfbd8fd172567bbc38d Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Wed, 19 Aug 2026 15:14:55 -0600 Subject: [PATCH 5/6] Update draftlog --- draftlogs/7959_fix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/draftlogs/7959_fix.md b/draftlogs/7959_fix.md index 4af4fbbf41c..83f894cd694 100644 --- a/draftlogs/7959_fix.md +++ b/draftlogs/7959_fix.md @@ -1 +1 @@ -- Fix numeric color sorting for bundled parallel-categories paths [[#7959](https://github.com/plotly/plotly.js/pull/7959)] +- Fix numeric color sorting for bundled parallel-categories (parcats) paths [[#7959](https://github.com/plotly/plotly.js/pull/7959)], with thanks to @CAOShurong for the contribution! From 49b904a0830c833eee8c20491b0bb32134663f8d Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Wed, 19 Aug 2026 15:19:53 -0600 Subject: [PATCH 6/6] Remove debug fit statement --- test/jasmine/tests/parcats_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/jasmine/tests/parcats_test.js b/test/jasmine/tests/parcats_test.js index 0aa41e83954..390d2b1d0ca 100644 --- a/test/jasmine/tests/parcats_test.js +++ b/test/jasmine/tests/parcats_test.js @@ -307,7 +307,7 @@ describe('Basic parcats trace', function() { .then(done, done.fail); }); - fit('should sort NaN color values after orderable values', function(done) { + it('should sort NaN color values after orderable values', function(done) { var trace = { type: 'parcats', dimensions: [