Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions src/EditorControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
shamefullyAdjustAxisRef,
shamefullyAdjustGeo,
shamefullyAddTableColumns,
shamefullyCreateSplitStyleProps,
shamefullyAdjustSplitStyleTargetContainers,
} from './shame';
import {EDITOR_ACTIONS} from './lib/constants';
import isNumeric from 'fast-isnumeric';
Expand Down Expand Up @@ -66,16 +68,32 @@ class EditorControls extends Component {
shamefullyClearAxisTypes(graphDiv, payload);
shamefullyAdjustAxisRef(graphDiv, payload);
shamefullyAddTableColumns(graphDiv, payload);
shamefullyAdjustSplitStyleTargetContainers(graphDiv, payload);

for (let i = 0; i < payload.traceIndexes.length; i++) {
for (const attr in payload.update) {
const traceIndex = payload.traceIndexes[i];
const prop = nestedProperty(graphDiv.data[traceIndex], attr);
const splitTraceGroup = payload.splitTraceGroup
? payload.splitTraceGroup.toString()
: null;

let props = [nestedProperty(graphDiv.data[traceIndex], attr)];
const value = payload.update[attr];

if (value !== void 0) {
prop.set(value);
if (splitTraceGroup) {
props = shamefullyCreateSplitStyleProps(
graphDiv,
attr,
traceIndex,
splitTraceGroup
);
}

props.forEach(p => {
if (value !== void 0) {
p.set(value);
}
});
}
}

Expand Down
195 changes: 126 additions & 69 deletions src/components/containers/TraceAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,109 @@ import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
const TraceFold = connectTraceToPlot(PlotlyFold);

class TraceAccordion extends Component {
render() {
const {data = [], localize: _} = this.context;
const {
canAdd,
canGroup,
children,
messageIfEmptyFold,
excludeFits,
} = this.props;
constructor(props, context) {
super(props, context);
this.setLocals(props, context);
}

componentWillReceiveProps(nextProps, nextContext) {
this.setLocals(nextProps, nextContext);
}

setLocals(props, context) {
// we don't want to include analysis transforms when we're in the create panel
const filteredData = data.filter(t => {
if (excludeFits) {
const base = props.canGroup ? context.fullData : context.data;

this.filteredTracesFullDataPositions = [];
this.filteredTraces = base.filter((t, i) => {
if (props.excludeFits) {
return !(t.transforms && t.transforms.every(tr => tr.type === 'fit'));
}
this.filteredTracesFullDataPositions.push(i);
return true;
});
}

const individualTraces =
filteredData.length &&
filteredData.map((d, i) => {
return (
<TraceFold
key={i}
traceIndexes={[i]}
canDelete={canAdd}
messageIfEmpty={messageIfEmptyFold}
>
{children}
</TraceFold>
);
});
renderGroupedTraceFolds() {
if (!this.filteredTraces.length || this.filteredTraces.length < 2) {
return null;
}

const dataArrayPositionsByTraceType = {};
const fullDataArrayPositionsByTraceType = {};

this.filteredTraces.forEach((trace, index) => {
const traceType = plotlyTraceToCustomTrace(trace);
if (!dataArrayPositionsByTraceType[traceType]) {
dataArrayPositionsByTraceType[traceType] = [];
}

if (!fullDataArrayPositionsByTraceType[traceType]) {
fullDataArrayPositionsByTraceType[traceType] = [];
}

dataArrayPositionsByTraceType[traceType].push(trace.index);
fullDataArrayPositionsByTraceType[traceType].push(
this.filteredTracesFullDataPositions[index]
);
});

return Object.keys(fullDataArrayPositionsByTraceType).map((type, index) => {
return (
<TraceFold
key={index}
traceIndexes={dataArrayPositionsByTraceType[type]}
name={type}
fullDataArrayPosition={fullDataArrayPositionsByTraceType[type]}
>
{this.props.children}
</TraceFold>
);
});
}

renderUngroupedTraceFolds() {
if (!this.filteredTraces.length) {
return null;
}

return this.filteredTraces.map((d, i) => {
return (
<TraceFold
key={i}
traceIndexes={[d.index]}
canDelete={this.props.canAdd}
messageIfEmpty={this.props.messageIfEmptyFold}
fullDataArrayPosition={[this.filteredTracesFullDataPositions[i]]}
>
{this.props.children}
</TraceFold>
);
});
}

renderTraceFolds() {
if (!this.filteredTraces.length) {
return null;
}

return this.filteredTraces.map((d, i) => {
return (
<TraceFold
key={i}
traceIndexes={[i]}
canDelete={this.props.canAdd}
messageIfEmpty={this.props.messageIfEmptyFold}
>
{this.props.children}
</TraceFold>
);
});
}

render() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice refactoring. This is a pretty hairy component and it's already looking a lot better! At some point I wonder if we can't break it apart more completely by having a GroupedTraceAccordion but not today.

const {canAdd, canGroup} = this.props;
const _ = this.context.localize;

if (canAdd) {
const addAction = {
Expand All @@ -54,58 +125,44 @@ class TraceAccordion extends Component {
}
},
};

return (
<PlotlyPanel addAction={addAction}>
{individualTraces ? individualTraces : null}
{this.renderTraceFolds()}
</PlotlyPanel>
);
}
const tracesByGroup = filteredData.reduce((allTraces, nextTrace, index) => {
const traceType = plotlyTraceToCustomTrace(nextTrace);
if (!allTraces[traceType]) {
allTraces[traceType] = [];
}
allTraces[traceType].push(index);
return allTraces;
}, {});

const groupedTraces = Object.keys(tracesByGroup).map((traceType, index) => {
return (
<TraceFold
key={index}
traceIndexes={tracesByGroup[traceType]}
name={traceType}
>
{this.props.children}
</TraceFold>
);
});
if (canGroup) {
if (this.filteredTraces.length === 1) {
return (
<TraceRequiredPanel>
{this.renderUngroupedTraceFolds()}
</TraceRequiredPanel>
);
}

if (canGroup && filteredData.length > 1 && groupedTraces.length > 0) {
return (
<TraceRequiredPanel noPadding>
<Tabs>
<TabList>
<Tab>{_('Individually')}</Tab>
<Tab>{_('By Type')}</Tab>
</TabList>
<TabPanel>
<PlotlyPanel>
{individualTraces ? individualTraces : null}
</PlotlyPanel>
</TabPanel>
<TabPanel>
<PlotlyPanel>{groupedTraces ? groupedTraces : null}</PlotlyPanel>
</TabPanel>
</Tabs>
</TraceRequiredPanel>
);
if (this.filteredTraces.length > 1) {
return (
<TraceRequiredPanel noPadding>
<Tabs>
<TabList>
<Tab>{_('Individually')}</Tab>
<Tab>{_('By Type')}</Tab>
</TabList>
<TabPanel>
<PlotlyPanel>{this.renderUngroupedTraceFolds()}</PlotlyPanel>
</TabPanel>
<TabPanel>
<PlotlyPanel>{this.renderGroupedTraceFolds()}</PlotlyPanel>
</TabPanel>
</Tabs>
</TraceRequiredPanel>
);
}
}
return (
<TraceRequiredPanel>
{individualTraces ? individualTraces : null}
</TraceRequiredPanel>
);

return <TraceRequiredPanel>{this.renderTraceFolds()}</TraceRequiredPanel>;
}
}

Expand Down
14 changes: 13 additions & 1 deletion src/components/containers/TransformAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,17 @@ class TransformAccordion extends Component {
</TransformFold>
));

// cannot have 2 Split transforms on one trace:
// https://github.com/plotly/plotly.js/issues/1742
const addActionOptions =
container.transforms &&
container.transforms.some(t => t.type === 'groupby')
? transformTypes.filter(t => t.type !== 'groupby')
: transformTypes;

const addAction = {
label: _('Transform'),
handler: transformTypes.map(({label, type}) => {
handler: addActionOptions.map(({label, type}) => {
return {
label,
handler: context => {
Expand All @@ -104,6 +112,10 @@ class TransformAccordion extends Component {
payload.groups = null;
}

if (type === 'groupby') {
payload.styles = [];
}

updateContainer({[key]: payload});
}
},
Expand Down
4 changes: 3 additions & 1 deletion src/components/fields/DataSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class UnconnectedDataSelector extends Component {
Array.isArray(this.fullValue);
}

this.hasData = props.attr in props.container;
this.hasData = props.container ? props.attr in props.container : false;
}

updatePlot(value) {
Expand Down Expand Up @@ -83,6 +83,7 @@ export class UnconnectedDataSelector extends Component {
: null,
}
);

this.props.updateContainer(update);
}

Expand Down Expand Up @@ -136,6 +137,7 @@ UnconnectedDataSelector.contextTypes = {
toSrc: PropTypes.func.isRequired,
fromSrc: PropTypes.func.isRequired,
}),
container: PropTypes.object,
};

function modifyPlotProps(props, context, plotProps) {
Expand Down
Loading