-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Transform react #2577
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
Transform react #2577
Changes from 1 commit
bbe3533
ed24765
fcc459d
7044a13
88b7b43
cf4c9c3
e84d4b9
b436d52
2a41f9e
965bcfb
6fae229
79295f1
5e9aa65
690eb95
a244cec
92bd5d2
dc6de2f
03956e1
f439e41
e47e6a9
aa30ad6
4c70826
7279b55
3e250df
cd08479
0414147
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -274,7 +274,7 @@ var extraFormatKeys = [ | |
// gd._fullLayout._transformModules | ||
// is a list of all the transform modules invoked. | ||
// | ||
plots.supplyDefaults = function(gd) { | ||
plots.supplyDefaults = function(gd, skipCalcUpdate) { | ||
var oldFullLayout = gd._fullLayout || {}; | ||
|
||
if(oldFullLayout._skipDefaults) { | ||
|
@@ -458,24 +458,28 @@ plots.supplyDefaults = function(gd) { | |
} | ||
|
||
// update object references in calcdata | ||
if(oldCalcdata.length === newFullData.length) { | ||
for(i = 0; i < newFullData.length; i++) { | ||
var newTrace = newFullData[i]; | ||
var cd0 = oldCalcdata[i][0]; | ||
if(cd0 && cd0.trace) { | ||
if(cd0.trace._hasCalcTransform) { | ||
remapTransformedArrays(cd0, newTrace); | ||
} else { | ||
cd0.trace = newTrace; | ||
} | ||
} | ||
} | ||
if(!skipCalcUpdate && oldCalcdata.length === newFullData.length) { | ||
plots.supplyDefaultsUpdateCalc(oldCalcdata, newFullData); | ||
} | ||
|
||
// sort base plot modules for consistent ordering | ||
newFullLayout._basePlotModules.sort(sortBasePlotModules); | ||
}; | ||
|
||
plots.supplyDefaultsUpdateCalc = function(oldCalcdata, newFullData) { | ||
for(var i = 0; i < newFullData.length; i++) { | ||
var newTrace = newFullData[i]; | ||
var cd0 = oldCalcdata[i][0]; | ||
if(cd0 && cd0.trace) { | ||
if(cd0.trace._hasCalcTransform) { | ||
remapTransformedArrays(cd0, newTrace); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. merged and 🐎 |
||
} else { | ||
cd0.trace = newTrace; | ||
} | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Make a container for collecting subplots we need to display. | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2955,6 +2955,190 @@ describe('Test plot api', function() { | |
.then(done); | ||
}); | ||
|
||
function aggregatedPie(i) { | ||
var labels = i <= 1 ? | ||
['A', 'B', 'A', 'C', 'A', 'B', 'C', 'A', 'B', 'C', 'A'] : | ||
['X', 'Y', 'Z', 'Z', 'Y', 'Z', 'X', 'Z', 'Y', 'Z', 'X']; | ||
var trace = { | ||
type: 'pie', | ||
values: [4, 1, 4, 4, 1, 4, 4, 2, 1, 1, 15], | ||
labels: labels, | ||
transforms: [{ | ||
type: 'aggregate', | ||
groups: labels, | ||
aggregations: [{target: 'values', func: 'sum'}] | ||
}] | ||
}; | ||
return { | ||
data: [trace], | ||
layout: { | ||
datarevision: i, | ||
colorway: ['red', 'orange', 'yellow', 'green', 'blue', 'violet'] | ||
} | ||
}; | ||
} | ||
|
||
var aggPie1CD = [[ | ||
{v: 26, label: 'A', color: 'red', i: 0}, | ||
{v: 9, label: 'C', color: 'orange', i: 2}, | ||
{v: 6, label: 'B', color: 'yellow', i: 1} | ||
]]; | ||
|
||
var aggPie2CD = [[ | ||
{v: 23, label: 'X', color: 'red', i: 0}, | ||
{v: 15, label: 'Z', color: 'orange', i: 2}, | ||
{v: 3, label: 'Y', color: 'yellow', i: 1} | ||
]]; | ||
|
||
function aggregatedScatter(i) { | ||
return { | ||
data: [{ | ||
x: [1, 2, 3, 4, 6, 5], | ||
y: [2, 1, 3, 5, 6, 4], | ||
transforms: [{ | ||
type: 'aggregate', | ||
groups: [1, -1, 1, -1, 1, -1], | ||
aggregations: i > 1 ? [{func: 'last', target: 'x'}] : [] | ||
}] | ||
}], | ||
layout: {daterevision: i + 10} | ||
}; | ||
} | ||
|
||
var aggScatter1CD = [[ | ||
{x: 1, y: 2, i: 0}, | ||
{x: 2, y: 1, i: 1} | ||
]]; | ||
|
||
var aggScatter2CD = [[ | ||
{x: 6, y: 2, i: 0}, | ||
{x: 5, y: 1, i: 1} | ||
]]; | ||
|
||
function aggregatedParcoords(i) { | ||
return { | ||
data: [{ | ||
type: 'parcoords', | ||
dimensions: [ | ||
{label: 'A', values: [1, 2, 3, 4]}, | ||
{label: 'B', values: [4, 3, 2, 1]} | ||
], | ||
transforms: i ? [{ | ||
type: 'aggregate', | ||
groups: [1, 2, 1, 2], | ||
aggregations: [ | ||
{target: 'dimensions[0].values', func: i > 1 ? 'avg' : 'first'}, | ||
{target: 'dimensions[1].values', func: i > 1 ? 'first' : 'avg'} | ||
] | ||
}] : | ||
[] | ||
}] | ||
}; | ||
} | ||
|
||
var aggParcoords0Vals = [[1, 2, 3, 4], [4, 3, 2, 1]]; | ||
var aggParcoords1Vals = [[1, 2], [3, 2]]; | ||
var aggParcoords2Vals = [[2, 3], [4, 3]]; | ||
|
||
function checkCalcData(expectedCD) { | ||
return function() { | ||
expect(gd.calcdata.length).toBe(expectedCD.length); | ||
expectedCD.forEach(function(expectedCDi, i) { | ||
var cdi = gd.calcdata[i]; | ||
expect(cdi.length).toBe(expectedCDi.length, i); | ||
expectedCDi.forEach(function(expectedij, j) { | ||
expect(cdi[j]).toEqual(jasmine.objectContaining(expectedij)); | ||
}); | ||
}); | ||
}; | ||
} | ||
|
||
function checkValues(expectedVals) { | ||
return function() { | ||
expect(gd._fullData.length).toBe(1); | ||
var dims = gd._fullData[0].dimensions; | ||
expect(dims.length).toBe(expectedVals.length); | ||
expectedVals.forEach(function(expected, i) { | ||
expect(dims[i].values).toEqual(expected); | ||
}); | ||
}; | ||
} | ||
|
||
function reactWith(fig) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very cute tests, especially this function name 💟 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... and I'm impressed that bbe3533 and standardizing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would actually name this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good call @nicolaskruchten -> |
||
return function() { return Plotly.react(gd, fig); }; | ||
} | ||
|
||
it('can change pie aggregations', function(done) { | ||
Plotly.newPlot(gd, aggregatedPie(1)) | ||
.then(checkCalcData(aggPie1CD)) | ||
|
||
.then(reactWith(aggregatedPie(2))) | ||
.then(checkCalcData(aggPie2CD)) | ||
|
||
.then(reactWith(aggregatedPie(1))) | ||
.then(checkCalcData(aggPie1CD)) | ||
.catch(failTest) | ||
.then(done); | ||
}); | ||
|
||
it('can change scatter aggregations', function(done) { | ||
Plotly.newPlot(gd, aggregatedScatter(1)) | ||
.then(checkCalcData(aggScatter1CD)) | ||
|
||
.then(reactWith(aggregatedScatter(2))) | ||
.then(checkCalcData(aggScatter2CD)) | ||
|
||
.then(reactWith(aggregatedScatter(1))) | ||
.then(checkCalcData(aggScatter1CD)) | ||
.catch(failTest) | ||
.then(done); | ||
}); | ||
|
||
it('can change parcoords aggregations', function(done) { | ||
Plotly.newPlot(gd, aggregatedParcoords(0)) | ||
.then(checkValues(aggParcoords0Vals)) | ||
|
||
.then(reactWith(aggregatedParcoords(1))) | ||
.then(checkValues(aggParcoords1Vals)) | ||
|
||
.then(reactWith(aggregatedParcoords(2))) | ||
.then(checkValues(aggParcoords2Vals)) | ||
|
||
.then(reactWith(aggregatedParcoords(0))) | ||
.then(checkValues(aggParcoords0Vals)) | ||
|
||
.catch(failTest) | ||
.then(done); | ||
}); | ||
|
||
it('can change type with aggregations', function(done) { | ||
Plotly.newPlot(gd, aggregatedScatter(1)) | ||
.then(checkCalcData(aggScatter1CD)) | ||
|
||
.then(reactWith(aggregatedPie(1))) | ||
.then(checkCalcData(aggPie1CD)) | ||
|
||
.then(reactWith(aggregatedParcoords(1))) | ||
.then(checkValues(aggParcoords1Vals)) | ||
|
||
.then(reactWith(aggregatedScatter(1))) | ||
.then(checkCalcData(aggScatter1CD)) | ||
|
||
.then(reactWith(aggregatedParcoords(2))) | ||
.then(checkValues(aggParcoords2Vals)) | ||
|
||
.then(reactWith(aggregatedPie(2))) | ||
.then(checkCalcData(aggPie2CD)) | ||
|
||
.then(reactWith(aggregatedScatter(2))) | ||
.then(checkCalcData(aggScatter2CD)) | ||
|
||
.then(reactWith(aggregatedParcoords(0))) | ||
.then(checkValues(aggParcoords0Vals)) | ||
.catch(failTest) | ||
.then(done); | ||
}); | ||
|
||
it('can change frames without redrawing', function(done) { | ||
var data = [{y: [1, 2, 3]}]; | ||
var layout = {}; | ||
|
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 not a big fan of this pattern.
At the very least we should turn this into
Plots.supplyDefaults(gd, {skipDefaultsUpdateCalc: 1})
to make it more readable and extendable.Personally, I'd vote for removing the newly added
Plots.supplyDefaultsUpdateCalc()
fromPlots.supplyDefaults
and callPlotly.supplyDefaultsUpdateCalc
afterPlots.supplyDefaults
in the places that new it.On
master
, we have:where the calls in
validate.js
andrangeslider/draw.js
(and maybe more) don't need to update calc.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.
Perhaps the name
supplyDefaultsUpdateCalc
isn't the best - though you could argue the same aboutcalcTransform
, which happens during thecalc
step but updates the data arrays in_fullData
.supplyDefaultsUpdateCalc
has one simple function when there are no transforms, to attach the new traces to the oldcalcdata
in case we're not going to run a recalc, but when there are transforms it also (first) pulls the transformed array attributes back from the oldfullTrace
to the new one. So I think in fact we do need this in most instances, and there are some external callers (like streambed) that depend on this as well.So I'm going to keep it as part of the default
supplyDefaults
behavior, but I'm happy to make it into an options object for clarity.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.
cleaner
supplyDefaults
API -> 2a41f9e