Skip to content

Commit f53f127

Browse files
authored
Merge pull request #747 from plotly/fits
Fit traces adjustments
2 parents 602c362 + 39fad65 commit f53f127

File tree

10 files changed

+132
-233
lines changed

10 files changed

+132
-233
lines changed

src/__tests__/PlotlyEditor.test.js

Lines changed: 0 additions & 74 deletions
This file was deleted.

src/components/containers/TraceAccordion.js

Lines changed: 68 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,27 @@ class TraceAccordion extends Component {
2121
}
2222

2323
setLocals(props, context) {
24-
// we don't want to include analysis transforms when we're in the create panel
2524
const base = props.canGroup ? context.fullData : context.data;
2625
const traceFilterCondition = this.props.traceFilterCondition || (() => true);
2726

28-
this.filteredTracesIndexes = [];
29-
this.filteredTraces = base.filter((t, i) => {
30-
if (traceFilterCondition(t, context.fullData[i])) {
31-
this.filteredTracesIndexes.push(i);
32-
return true;
33-
}
34-
return false;
35-
});
27+
this.filteredTracesDataIndexes = [];
28+
this.filteredTraces = [];
29+
30+
if (base && base.length && context.fullData.length) {
31+
this.filteredTraces = base.filter((t, i) => {
32+
const fullTrace = props.canGroup ? t : context.fullData.filter(tr => tr.index === i)[0];
33+
34+
if (fullTrace) {
35+
const trace = context.data[fullTrace.index];
36+
if (traceFilterCondition(trace, fullTrace)) {
37+
this.filteredTracesDataIndexes.push(fullTrace.index);
38+
return true;
39+
}
40+
}
41+
42+
return false;
43+
});
44+
}
3645
}
3746

3847
renderGroupedTraceFolds() {
@@ -44,7 +53,7 @@ class TraceAccordion extends Component {
4453
const dataArrayPositionsByTraceType = {};
4554
const fullDataArrayPositionsByTraceType = {};
4655

47-
this.filteredTraces.forEach((trace, index) => {
56+
this.filteredTraces.forEach(trace => {
4857
const traceType = plotlyTraceToCustomTrace(trace);
4958
if (!dataArrayPositionsByTraceType[traceType]) {
5059
dataArrayPositionsByTraceType[traceType] = [];
@@ -55,7 +64,8 @@ class TraceAccordion extends Component {
5564
}
5665

5766
dataArrayPositionsByTraceType[traceType].push(trace.index);
58-
fullDataArrayPositionsByTraceType[traceType].push(this.filteredTracesIndexes[index]);
67+
// _expandedIndex is the trace's index in the fullData array
68+
fullDataArrayPositionsByTraceType[traceType].push(trace._expandedIndex);
5969
});
6070

6171
return Object.keys(fullDataArrayPositionsByTraceType).map((type, index) => (
@@ -71,28 +81,54 @@ class TraceAccordion extends Component {
7181
}
7282

7383
renderUngroupedTraceFolds() {
74-
return this.filteredTraces.map((d, i) => (
75-
<TraceFold
76-
key={i}
77-
traceIndexes={[d.index]}
78-
canDelete={this.props.canAdd}
79-
fullDataArrayPosition={[this.filteredTracesIndexes[i]]}
80-
>
81-
{this.props.children}
82-
</TraceFold>
83-
));
84+
if (this.filteredTraces.length) {
85+
return this.filteredTraces.map((d, i) => (
86+
<TraceFold
87+
key={i}
88+
traceIndexes={[d.index]}
89+
canDelete={this.props.canAdd}
90+
fullDataArrayPosition={[d._expandedIndex]}
91+
>
92+
{this.props.children}
93+
</TraceFold>
94+
));
95+
}
96+
return null;
8497
}
8598

8699
renderTraceFolds() {
87-
return this.filteredTraces.map((d, i) => (
88-
<TraceFold
89-
key={i}
90-
traceIndexes={[this.filteredTracesIndexes[i]]}
91-
canDelete={this.props.canAdd}
92-
>
93-
{this.props.children}
94-
</TraceFold>
95-
));
100+
if (this.filteredTraces.length) {
101+
return this.filteredTraces.map((d, i) => (
102+
<TraceFold
103+
key={i}
104+
traceIndexes={[this.filteredTracesDataIndexes[i]]}
105+
canDelete={this.props.canAdd}
106+
>
107+
{this.props.children}
108+
</TraceFold>
109+
));
110+
}
111+
return null;
112+
}
113+
114+
renderTracePanelHelp() {
115+
const _ = this.context.localize;
116+
return (
117+
<div className="panel__empty__message">
118+
<div className="panel__empty__message__heading">Trace your data.</div>
119+
<div className="panel__empty__message__content">
120+
<p>
121+
{_('Traces of various types like bar and line are the building blocks of your figure.')}
122+
</p>
123+
<p>
124+
{_(
125+
'You can add as many as you like, mixing and matching types and arranging them into subplots.'
126+
)}
127+
</p>
128+
<p>{_('Click on the + button above to add a trace.')}</p>
129+
</div>
130+
</div>
131+
);
96132
}
97133

98134
render() {
@@ -110,29 +146,10 @@ class TraceAccordion extends Component {
110146
}
111147
},
112148
};
113-
const content = this.renderTraceFolds();
149+
const traceFolds = this.renderTraceFolds();
114150
return (
115151
<PlotlyPanel addAction={addAction}>
116-
{content.length ? (
117-
content
118-
) : (
119-
<div className="panel__empty__message">
120-
<div className="panel__empty__message__heading">Trace your data.</div>
121-
<div className="panel__empty__message__content">
122-
<p>
123-
{_(
124-
'Traces of various types like bar and line are the building blocks of your figure.'
125-
)}
126-
</p>
127-
<p>
128-
{_(
129-
'You can add as many as you like, mixing and matching types and arranging them into subplots.'
130-
)}
131-
</p>
132-
<p>{_('Click on the + button above to add a trace.')}</p>
133-
</div>
134-
</div>
135-
)}
152+
{traceFolds ? traceFolds : this.renderTracePanelHelp()}
136153
</PlotlyPanel>
137154
);
138155
}

src/lib/connectAxesToLayout.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default function connectAxesToLayout(WrappedComponent) {
7474
const multiValuedContainer = deepCopyPublic(this.axes[0]);
7575
this.axes.slice(1).forEach(ax =>
7676
Object.keys(ax).forEach(key =>
77-
setMultiValuedContainer(multiValuedContainer, ax, key, {
77+
setMultiValuedContainer(multiValuedContainer, deepCopyPublic(ax), key, {
7878
searchArrays: true,
7979
})
8080
)

src/lib/connectCartesianSubplotToLayout.js

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {Component} from 'react';
22
import PropTypes from 'prop-types';
3-
import {getDisplayName, plotlyTraceToCustomTrace, renderTraceIcon} from '../lib';
3+
import {getDisplayName, plotlyTraceToCustomTrace, renderTraceIcon, getFullTrace} from '../lib';
44

55
export default function connectCartesianSubplotToLayout(WrappedComponent) {
66
class SubplotConnectedComponent extends Component {
@@ -17,7 +17,7 @@ export default function connectCartesianSubplotToLayout(WrappedComponent) {
1717

1818
setLocals(props, context) {
1919
const {xaxis, yaxis, traceIndexes} = props;
20-
const {container, fullContainer, data, fullData} = context;
20+
const {container, fullContainer, data} = context;
2121

2222
this.container = {
2323
xaxis: container[xaxis],
@@ -29,33 +29,7 @@ export default function connectCartesianSubplotToLayout(WrappedComponent) {
2929
};
3030

3131
const trace = traceIndexes.length > 0 ? data[traceIndexes[0]] : {};
32-
let fullTrace = {};
33-
for (let i = 0; i < fullData.length; i++) {
34-
if (traceIndexes[0] === fullData[i]._fullInput.index) {
35-
/*
36-
* Fit transforms are custom transforms in our custom plotly.js bundle,
37-
* they are different from others as they create an extra trace in the
38-
* data array. When plotly.js runs supplyTraceDefaults (before the
39-
* transforms code executes) it stores the result in _fullInput,
40-
* so that we have a reference to what the original, corrected input was.
41-
* Then the transform code runs, our figure changes accordingly, but
42-
* we're still able to use the original input as it's in _fullInput.
43-
* This is the desired behaviour for our transforms usually,
44-
* but it is not useful for fits, as the transform code adds some styles
45-
* that are useful for the trace, so really for fits we'd like to read
46-
* from _fullData, not _fullInput. Here we're setting _fullInput to
47-
* _fullData as that is where the rest of our code expects to find its
48-
* values.
49-
*/
50-
if (trace.transforms && trace.transforms.every(t => t.type === 'fit')) {
51-
fullData[i]._fullInput = fullData[i];
52-
}
53-
54-
fullTrace = fullData[i]._fullInput;
55-
56-
break;
57-
}
58-
}
32+
const fullTrace = getFullTrace(props, context);
5933

6034
if (trace && fullTrace) {
6135
this.icon = renderTraceIcon(plotlyTraceToCustomTrace(trace));

src/lib/connectNonCartesianSubplotToLayout.js

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, {Component} from 'react';
22
import PropTypes from 'prop-types';
3-
import {getDisplayName, plotlyTraceToCustomTrace, renderTraceIcon} from '../lib';
3+
import {getDisplayName, plotlyTraceToCustomTrace, renderTraceIcon, getFullTrace} from '../lib';
44

55
export default function connectNonCartesianSubplotToLayout(WrappedComponent) {
66
class SubplotConnectedComponent extends Component {
@@ -17,39 +17,13 @@ export default function connectNonCartesianSubplotToLayout(WrappedComponent) {
1717

1818
setLocals(props, context) {
1919
const {subplot, traceIndexes} = props;
20-
const {container, fullContainer, data, fullData} = context;
20+
const {container, fullContainer, data} = context;
2121

2222
this.container = container[subplot] || {};
2323
this.fullContainer = fullContainer[subplot] || {};
2424

2525
const trace = traceIndexes.length > 0 ? data[traceIndexes[0]] : {};
26-
let fullTrace = {};
27-
for (let i = 0; i < fullData.length; i++) {
28-
if (traceIndexes[0] === fullData[i]._fullInput.index) {
29-
/*
30-
* Fit transforms are custom transforms in our custom plotly.js bundle,
31-
* they are different from others as they create an extra trace in the
32-
* data array. When plotly.js runs supplyTraceDefaults (before the
33-
* transforms code executes) it stores the result in _fullInput,
34-
* so that we have a reference to what the original, corrected input was.
35-
* Then the transform code runs, our figure changes accordingly, but
36-
* we're still able to use the original input as it's in _fullInput.
37-
* This is the desired behaviour for our transforms usually,
38-
* but it is not useful for fits, as the transform code adds some styles
39-
* that are useful for the trace, so really for fits we'd like to read
40-
* from _fullData, not _fullInput. Here we're setting _fullInput to
41-
* _fullData as that is where the rest of our code expects to find its
42-
* values.
43-
*/
44-
if (trace.transforms && trace.transforms.every(t => t.type === 'fit')) {
45-
fullData[i]._fullInput = fullData[i];
46-
}
47-
48-
fullTrace = fullData[i]._fullInput;
49-
50-
break;
51-
}
52-
}
26+
const fullTrace = getFullTrace(props, context);
5327

5428
if (trace && fullTrace) {
5529
this.icon = renderTraceIcon(plotlyTraceToCustomTrace(trace));

0 commit comments

Comments
 (0)