Skip to content

Commit a061c56

Browse files
Grouped Trace tabs (#312)
1 parent 1e62588 commit a061c56

24 files changed

+258
-79
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"react-colorscales": "^0.4.2",
2222
"react-dom": "^16.2.0",
2323
"react-select": "^1.0.0-rc.10",
24+
"react-tabs": "^2.2.1",
2425
"tinycolor2": "^1.4.1"
2526
},
2627
"devDependencies": {

src/components/containers/Panel.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ class Panel extends Component {
106106
);
107107

108108
return (
109-
<div className={bem('panel')}>
109+
<div
110+
className={`panel${this.props.noPadding ? ' panel--no-padding' : ''}`}
111+
>
110112
<PanelHeader
111113
addAction={this.props.addAction}
112114
allowCollapse={
@@ -125,6 +127,7 @@ Panel.propTypes = {
125127
children: PropTypes.node,
126128
addAction: PropTypes.object,
127129
showExpandCollapse: PropTypes.bool,
130+
noPadding: PropTypes.bool,
128131
};
129132

130133
Panel.defaultProps = {

src/components/containers/TraceAccordion.js

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,29 @@ import Panel from './Panel';
44
import PropTypes from 'prop-types';
55
import React, {Component} from 'react';
66
import {EDITOR_ACTIONS} from 'lib/constants';
7-
import {connectTraceToPlot, localize} from 'lib';
7+
import {connectTraceToPlot, localize, plotlyTraceToCustomTrace} from 'lib';
8+
import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
89

910
const TraceFold = connectTraceToPlot(Fold);
1011

1112
class TraceAccordion extends Component {
1213
render() {
13-
const {data = []} = this.context;
14-
const {canAdd, children, messageIfEmptyFold, localize: _} = this.props;
14+
const {data = [], fullData = []} = this.context;
15+
const {
16+
canAdd,
17+
canGroup,
18+
children,
19+
messageIfEmptyFold,
20+
localize: _,
21+
} = this.props;
1522

16-
const content =
23+
const individualTraces =
1724
data.length &&
1825
data.map((d, i) => {
1926
return (
2027
<TraceFold
2128
key={i}
22-
traceIndex={i}
29+
traceIndexes={[i]}
2330
canDelete={canAdd}
2431
messageIfEmpty={messageIfEmptyFold}
2532
>
@@ -39,20 +46,72 @@ class TraceAccordion extends Component {
3946
}
4047
},
4148
};
42-
return <Panel addAction={addAction}>{content ? content : null}</Panel>;
49+
return (
50+
<Panel addAction={addAction}>
51+
{individualTraces ? individualTraces : null}
52+
</Panel>
53+
);
4354
}
44-
return <TraceRequiredPanel>{content ? content : null}</TraceRequiredPanel>;
55+
if (canGroup && data.length > 1) {
56+
const tracesByGroup = data.reduce((allTraces, next, index) => {
57+
const traceType = plotlyTraceToCustomTrace(
58+
fullData.filter(trace => trace.index === index)[0]
59+
);
60+
if (!allTraces[traceType]) {
61+
allTraces[traceType] = [];
62+
}
63+
allTraces[traceType].push(index);
64+
return allTraces;
65+
}, {});
66+
67+
const groupedTraces = Object.keys(tracesByGroup).map(
68+
(traceType, index) => {
69+
return (
70+
<TraceFold
71+
key={index}
72+
traceIndexes={tracesByGroup[traceType]}
73+
name={traceType}
74+
>
75+
{this.props.children}
76+
</TraceFold>
77+
);
78+
}
79+
);
80+
return (
81+
<TraceRequiredPanel noPadding>
82+
<Tabs>
83+
<TabList>
84+
<Tab>{_('All Traces')}</Tab>
85+
<Tab>{_('Individual')}</Tab>
86+
</TabList>
87+
<TabPanel>
88+
<Panel>{groupedTraces ? groupedTraces : null}</Panel>
89+
</TabPanel>
90+
<TabPanel>
91+
<Panel>{individualTraces ? individualTraces : null}</Panel>
92+
</TabPanel>
93+
</Tabs>
94+
</TraceRequiredPanel>
95+
);
96+
}
97+
return (
98+
<TraceRequiredPanel>
99+
{individualTraces ? individualTraces : null}
100+
</TraceRequiredPanel>
101+
);
45102
}
46103
}
47104

48105
TraceAccordion.contextTypes = {
106+
fullData: PropTypes.array,
49107
data: PropTypes.array,
50108
};
51109

52110
TraceAccordion.propTypes = {
53111
localize: PropTypes.func,
54112
children: PropTypes.node,
55113
canAdd: PropTypes.bool,
114+
canGroup: PropTypes.bool,
56115
messageIfEmptyFold: PropTypes.string,
57116
};
58117

src/components/containers/__tests__/Fold-test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('<Fold>', () => {
1111
const withoutDelete = mount(
1212
<TestEditor {...fixtures.scatter()}>
1313
<Panel>
14-
<Fold foldIndex={0}>
14+
<Fold>
1515
<Numeric attr="opacity" />
1616
</Fold>
1717
</Panel>
@@ -22,7 +22,7 @@ describe('<Fold>', () => {
2222
const withDelete = mount(
2323
<TestEditor {...fixtures.scatter()}>
2424
<Panel>
25-
<TraceFold traceIndex={0} canDelete={true} foldIndex={0}>
25+
<TraceFold traceIndexes={[0]} canDelete={true}>
2626
<Numeric attr="opacity" />
2727
</TraceFold>
2828
</Panel>
@@ -36,7 +36,7 @@ describe('<Fold>', () => {
3636
mount(
3737
<TestEditor {...fixtures.scatter()} beforeDeleteTrace={beforeDeleteTrace}>
3838
<Panel>
39-
<TraceFold traceIndex={0} canDelete={true} foldIndex={0}>
39+
<TraceFold traceIndexes={[0]} canDelete={true}>
4040
<Numeric attr="opacity" />
4141
</TraceFold>
4242
</Panel>

src/components/containers/__tests__/Layout-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Layouts.forEach(Layout => {
1515
const wrapper = mount(
1616
<Editor {...fixtures.scatter({layout: {width: 100}})}>
1717
<Panel>
18-
<Layout foldIndex={0}>
18+
<Layout>
1919
<Numeric label="Width" min={100} step={10} attr="width" />
2020
</Layout>
2121
</Panel>
@@ -35,7 +35,7 @@ Layouts.forEach(Layout => {
3535
{...fixtures.scatter({layout: {width: 100}})}
3636
>
3737
<Panel>
38-
<Layout foldIndex={0}>
38+
<Layout>
3939
<Numeric label="Width" min={100} step={10} attr="width" />
4040
</Layout>
4141
</Panel>

src/components/containers/__tests__/Section-test.js

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe('Section', () => {
1414
// mode is visible with scatter. Hole is not visible. Section should show.
1515
const wrapper = mount(
1616
<TestEditor onUpdate={jest.fn()} {...fixtures.scatter()}>
17-
<TraceSection traceIndex={0} name="test-section">
17+
<TraceSection traceIndexes={[0]} name="test-section">
1818
<Flaglist
1919
attr="mode"
2020
options={[
@@ -64,9 +64,9 @@ describe('Section', () => {
6464
// pull and hole are not scatter attrs. Section should not show.
6565
const wrapper = mount(
6666
<TestEditor onUpdate={jest.fn()} {...fixtures.scatter()}>
67-
<TraceSection traceIndex={0} name="test-section">
68-
<Numeric attr="pull" min={0} max={1} step={0.1} traceIndex={0} />
69-
<Numeric attr="hole" min={0} max={1} step={0.1} traceIndex={0} />
67+
<TraceSection traceIndexes={[0]} name="test-section">
68+
<Numeric attr="pull" min={0} max={1} step={0.1} traceIndexes={[0]} />
69+
<Numeric attr="hole" min={0} max={1} step={0.1} traceIndexes={[0]} />
7070
</TraceSection>
7171
</TestEditor>
7272
)
@@ -85,8 +85,8 @@ describe('Section', () => {
8585
const TraceSection = connectTraceToPlot(Section);
8686
const wrapper = mount(
8787
<TestEditor onUpdate={jest.fn()} {...fixtures.scatter()}>
88-
<TraceSection name="test-section" traceIndex={0}>
89-
<Numeric attr="opacity" traceIndex={0} />
88+
<TraceSection name="test-section" traceIndexes={[0]}>
89+
<Numeric attr="opacity" traceIndexes={[0]} />
9090
<MenuPanel show>
9191
<Info>INFO</Info>
9292
</MenuPanel>
@@ -106,8 +106,8 @@ describe('Section', () => {
106106
const TraceSection = connectTraceToPlot(Section);
107107
const wrapper = mount(
108108
<TestEditor onUpdate={jest.fn()} {...fixtures.scatter()}>
109-
<TraceSection name="test-section" traceIndex={0}>
110-
<Numeric attr="badattr" traceIndex={0} />
109+
<TraceSection name="test-section" traceIndexes={[0]}>
110+
<Numeric attr="badattr" traceIndexes={[0]} />
111111
<MenuPanel show>
112112
<Info>INFO</Info>
113113
</MenuPanel>
@@ -123,8 +123,8 @@ describe('Section', () => {
123123
const TraceSection = connectTraceToPlot(Section);
124124
const wrapper = mount(
125125
<TestEditor onUpdate={jest.fn()} {...fixtures.scatter()}>
126-
<TraceSection name="test-section" traceIndex={0}>
127-
<Numeric attr="badattr" traceIndex={0} />
126+
<TraceSection name="test-section" traceIndexes={[0]}>
127+
<Numeric attr="badattr" traceIndexes={[0]} />
128128
<Info>INFO</Info>
129129
</TraceSection>
130130
</TestEditor>
@@ -141,7 +141,7 @@ describe('TraceTypeSection', () => {
141141
<TestEditor onUpdate={jest.fn()} {...fixtures.scatter()}>
142142
<TraceSection
143143
name="test-section"
144-
traceIndex={0}
144+
traceIndexes={[0]}
145145
traceTypes={['scatter']}
146146
>
147147
<Flaglist
@@ -164,7 +164,7 @@ describe('TraceTypeSection', () => {
164164
<TestEditor onUpdate={jest.fn()} {...fixtures.scatter()}>
165165
<TraceSection
166166
name="test-section"
167-
traceIndex={0}
167+
traceIndexes={[0]}
168168
traceTypes={['heatmap']}
169169
>
170170
<Flaglist

src/components/fields/TraceSelector.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,14 @@ class TraceSelector extends Component {
113113
} else {
114114
this.traceOptions = [{label: _('Scatter'), value: 'scatter'}];
115115
}
116-
this.fullValue = plotlyTraceToCustomTrace(props.container);
116+
if (props.container) {
117+
this.fullValue = plotlyTraceToCustomTrace(props.container);
118+
}
117119
}
118120

119121
setTraceDefaults(container, fullContainer, updateContainer) {
120122
if (
123+
container &&
121124
container.uid &&
122125
!container.mode &&
123126
fullContainer._fullInput.type === 'scatter'

src/components/fields/__tests__/CanvasSize-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('CanvasSize', () => {
1313
const wrapper = mount(
1414
<TestEditor {...{...fixtureProps}}>
1515
<LayoutPanel name="Layout">
16-
<Fold name="Canvas" foldIndex={0}>
16+
<Fold name="Canvas">
1717
<CanvasSize attr="width" />
1818
</Fold>
1919
</LayoutPanel>
@@ -28,7 +28,7 @@ describe('CanvasSize', () => {
2828
const wrapper = mount(
2929
<TestEditor {...{...fixtureProps}}>
3030
<LayoutPanel name="Layout">
31-
<Fold name="Canvas" foldIndex={0}>
31+
<Fold name="Canvas">
3232
<CanvasSize attr="width" />
3333
</Fold>
3434
</LayoutPanel>

src/components/fields/__tests__/DataSelector-test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ function render(overrides = {}, children) {
1616
{children}
1717
</TestEditor>
1818
)
19-
.find(`[traceIndex=1]`)
2019
.find(`[attr="${attr}"]`)
2120
.last();
2221
}
@@ -64,7 +63,7 @@ describe('DataSelector', () => {
6463
const wrapper = render(
6564
{},
6665
<TraceDataSelector
67-
traceIndex={1}
66+
traceIndexes={[0]}
6867
label={{pie: 'hodor', '*': 'rodoh'}}
6968
attr="x"
7069
/>

src/components/fields/__tests__/Radio-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('<Radio>', () => {
1212
it('enables <Field> centering by default', () => {
1313
const wrapper = mount(
1414
<TestEditor plotly={plotly} {...fixtures.area()}>
15-
<Trace traceIndex={0}>
15+
<Trace traceIndexes={[0]}>
1616
<Radio
1717
label="Connect Gaps"
1818
attr="connectgaps"
@@ -31,7 +31,7 @@ describe('<Radio>', () => {
3131
it('permits <Field> centering to be disabled', () => {
3232
const wrapper = mount(
3333
<TestEditor plotly={plotly} {...fixtures.area()}>
34-
<Trace traceIndex={0}>
34+
<Trace traceIndexes={[0]}>
3535
<Radio
3636
center={false}
3737
label="Connect Gaps"

src/components/fields/__tests__/TraceSelector-test.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe('TraceSelector', () => {
1616
};
1717
const wrapper = mount(
1818
<TestEditor {...editorProps} plotly={plotly}>
19-
<TraceSection traceIndex={0}>
19+
<TraceSection traceIndexes={[0]}>
2020
<TraceSelector attr="type" />
2121
</TraceSection>
2222
</TestEditor>
@@ -37,7 +37,7 @@ describe('TraceSelector', () => {
3737
};
3838
const wrapper = mount(
3939
<TestEditor {...editorProps} plotly={plotly}>
40-
<TraceSection traceIndex={0}>
40+
<TraceSection traceIndexes={[0]}>
4141
<TraceSelector attr="type" />
4242
</TraceSection>
4343
</TestEditor>
@@ -59,7 +59,7 @@ describe('TraceSelector', () => {
5959

6060
const wrapper = mount(
6161
<TestEditor {...editorProps} plotly={plotly}>
62-
<TraceSection traceIndex={0}>
62+
<TraceSection traceIndexes={[0]}>
6363
<TraceSelector attr="type" />
6464
</TraceSection>
6565
</TestEditor>
@@ -77,7 +77,7 @@ describe('TraceSelector', () => {
7777
};
7878
const wrapper = mount(
7979
<TestEditor {...editorProps} plotly={plotly}>
80-
<TraceSection traceIndex={0}>
80+
<TraceSection traceIndexes={[0]}>
8181
<TraceSelector attr="type" />
8282
</TraceSection>
8383
</TestEditor>
@@ -95,7 +95,7 @@ describe('TraceSelector', () => {
9595
};
9696
const wrapper = mount(
9797
<TestEditor {...editorProps} plotly={plotly}>
98-
<TraceSection traceIndex={0}>
98+
<TraceSection traceIndexes={[0]}>
9999
<TraceSelector attr="type" />
100100
</TraceSection>
101101
</TestEditor>
@@ -113,7 +113,7 @@ describe('TraceSelector', () => {
113113
};
114114
const wrapper = mount(
115115
<TestEditor {...editorProps} plotly={plotly}>
116-
<TraceSection traceIndex={0}>
116+
<TraceSection traceIndexes={[0]}>
117117
<TraceSelector attr="type" />
118118
</TraceSection>
119119
</TestEditor>
@@ -133,7 +133,7 @@ describe('TraceSelector', () => {
133133
};
134134
const wrapper = mount(
135135
<TestEditor {...editorProps}>
136-
<TraceSection traceIndex={0}>
136+
<TraceSection traceIndexes={[0]}>
137137
<TraceSelector attr="type" />
138138
</TraceSection>
139139
</TestEditor>
@@ -159,7 +159,7 @@ describe('TraceSelector', () => {
159159
};
160160
const wrapper = mount(
161161
<TestEditor {...editorProps}>
162-
<TraceSection traceIndex={0}>
162+
<TraceSection traceIndexes={[0]}>
163163
<TraceSelector attr="type" />
164164
</TraceSection>
165165
</TestEditor>

0 commit comments

Comments
 (0)