Skip to content

Commit 4166fb4

Browse files
authored
Merge pull request #692 from plotly/empty-panels
Empty panels
2 parents ad72272 + 5f08d6b commit 4166fb4

13 files changed

+132
-121
lines changed

src/DefaultEditor.js

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,58 @@ import {
1717
StyleUpdateMenusPanel,
1818
} from './default_panels';
1919
import Logo from './components/widgets/Logo';
20+
import {TRANSFORMABLE_TRACES} from './lib/constants';
2021

2122
class DefaultEditor extends Component {
23+
constructor(props, context) {
24+
super(props, context);
25+
this.hasTransforms = this.hasTransforms.bind(this);
26+
this.hasAxes = this.hasAxes.bind(this);
27+
this.hasMenus = this.hasMenus.bind(this);
28+
this.hasSliders = this.hasSliders.bind(this);
29+
this.hasColorbars = this.hasColorbars.bind(this);
30+
}
31+
32+
hasTransforms() {
33+
return this.context.fullData.some(d =>
34+
TRANSFORMABLE_TRACES.includes(d.type)
35+
);
36+
}
37+
38+
hasAxes() {
39+
return (
40+
Object.keys(this.context.fullLayout._subplots).filter(
41+
type =>
42+
!['cartesian', 'mapbox'].includes(type) &&
43+
this.context.fullLayout._subplots[type].length > 0
44+
).length > 0
45+
);
46+
}
47+
48+
hasMenus() {
49+
const {
50+
fullLayout: {updatemenus = []},
51+
} = this.context;
52+
53+
return updatemenus.length > 0;
54+
}
55+
56+
hasSliders() {
57+
const {
58+
layout: {sliders = []},
59+
} = this.context;
60+
61+
return sliders.length > 0;
62+
}
63+
64+
hasColorbars() {
65+
return this.context.fullData.some(
66+
d =>
67+
(d.marker && d.marker.showscale !== undefined) || // eslint-disable-line no-undefined
68+
d.showscale !== undefined // eslint-disable-line no-undefined
69+
);
70+
}
71+
2272
render() {
2373
const _ = this.context.localize;
2474
const logo = this.props.logoSrc && <Logo src={this.props.logoSrc} />;
@@ -28,17 +78,27 @@ class DefaultEditor extends Component {
2878
{logo ? logo : null}
2979
<GraphCreatePanel group={_('Graph')} name={_('Create')} />
3080
<GraphSubplotsPanel group={_('Graph')} name={_('Subplots')} />
31-
<GraphTransformsPanel group={_('Graph')} name={_('Transforms')} />
81+
{this.hasTransforms() && (
82+
<GraphTransformsPanel group={_('Graph')} name={_('Transforms')} />
83+
)}
3284
<StyleTracesPanel group={_('Style')} name={_('Traces')} />
3385
<StyleLayoutPanel group={_('Style')} name={_('Layout')} />
34-
<StyleAxesPanel group={_('Style')} name={_('Axes')} />
86+
{this.hasAxes() && (
87+
<StyleAxesPanel group={_('Style')} name={_('Axes')} />
88+
)}
3589
<StyleLegendPanel group={_('Style')} name={_('Legend')} />
36-
<StyleColorbarsPanel group={_('Style')} name={_('Color Bars')} />
90+
{this.hasColorbars() && (
91+
<StyleColorbarsPanel group={_('Style')} name={_('Color Bars')} />
92+
)}
3793
<StyleNotesPanel group={_('Style')} name={_('Annotations')} />
3894
<StyleShapesPanel group={_('Style')} name={_('Shapes')} />
3995
<StyleImagesPanel group={_('Style')} name={_('Images')} />
40-
<StyleSlidersPanel group={_('Style')} name={_('Sliders')} />
41-
<StyleUpdateMenusPanel group={_('Style')} name={_('Menus')} />
96+
{this.hasSliders() && (
97+
<StyleSlidersPanel group={_('Style')} name={_('Sliders')} />
98+
)}
99+
{this.hasMenus() && (
100+
<StyleUpdateMenusPanel group={_('Style')} name={_('Menus')} />
101+
)}
42102
{this.props.children ? this.props.children : null}
43103
</PanelMenuWrapper>
44104
);
@@ -52,6 +112,9 @@ DefaultEditor.propTypes = {
52112

53113
DefaultEditor.contextTypes = {
54114
localize: PropTypes.func,
115+
fullData: PropTypes.array,
116+
fullLayout: PropTypes.object,
117+
layout: PropTypes.object,
55118
};
56119

57120
export default DefaultEditor;

src/components/PanelMenuWrapper.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ class PanelsWithSidebar extends Component {
2323
this.setState({group, panel});
2424
}
2525

26+
getChildContext() {
27+
return {
28+
setPanel: this.setPanel,
29+
};
30+
}
31+
2632
renderSection(section, i) {
2733
if (
2834
section.type &&
@@ -100,4 +106,8 @@ PanelsWithSidebar.propTypes = {
100106
children: PropTypes.node,
101107
};
102108

109+
PanelsWithSidebar.childContextTypes = {
110+
setPanel: PropTypes.func,
111+
};
112+
103113
export default PanelsWithSidebar;

src/components/containers/PanelEmpty.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ class PanelEmpty extends Component {
77
render() {
88
const {children, icon: Icon} = this.props;
99
const heading = this.props.heading || '';
10-
const message = this.props.message || '';
1110

1211
return (
1312
<div className={bem('panel', 'empty')}>
@@ -16,10 +15,7 @@ class PanelEmpty extends Component {
1615
{Icon ? <Icon /> : <ChartLineIcon />}
1716
</div>
1817
<div className="panel__empty__message__heading">{heading}</div>
19-
<div className="panel__empty__message__content">
20-
<p>{message}</p>
21-
{children}
22-
</div>
18+
<div className="panel__empty__message__content">{children}</div>
2319
</div>
2420
</div>
2521
);
@@ -28,7 +24,6 @@ class PanelEmpty extends Component {
2824

2925
PanelEmpty.propTypes = {
3026
heading: PropTypes.string,
31-
message: PropTypes.any,
3227
children: PropTypes.node,
3328
icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
3429
};

src/components/containers/SliderAccordion.js

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,7 @@ class SliderAccordion extends Component {
2222
</SliderFold>
2323
));
2424

25-
return (
26-
<TraceRequiredPanel
27-
extraConditions={[() => sliders.length > 0]}
28-
extraEmptyPanelMessages={[
29-
{
30-
heading: _('There are no sliders to style in your plot'),
31-
message: '',
32-
},
33-
]}
34-
>
35-
{content ? content : null}
36-
</TraceRequiredPanel>
37-
);
25+
return <TraceRequiredPanel>{content ? content : null}</TraceRequiredPanel>;
3826
}
3927
}
4028

src/components/containers/TraceRequiredPanel.js

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,56 +11,32 @@ class TraceRequiredPanel extends Component {
1111
render() {
1212
const {localize: _} = this.context;
1313
const {children, ...rest} = this.props;
14-
let showPanel = true;
15-
const emptyPanelMessage = {heading: '', message: ''};
1614

17-
const noTraceMessage = {
18-
heading: _("Looks like there aren't any traces defined yet."),
19-
message: _("Go to the 'Create' tab to define traces."),
20-
};
21-
22-
const conditions = [() => this.hasTrace()].concat(
23-
this.props.extraConditions ? this.props.extraConditions : []
24-
);
15+
if (!this.props.visible) {
16+
return null;
17+
}
2518

26-
const messages = [noTraceMessage].concat(
27-
this.props.extraEmptyPanelMessages
28-
? this.props.extraEmptyPanelMessages
29-
: []
19+
return this.hasTrace() ? (
20+
<LayoutPanel {...rest}>{children}</LayoutPanel>
21+
) : (
22+
<PanelEmpty
23+
heading={_("Looks like there aren't any traces defined yet.")}
24+
>
25+
<p>
26+
{_('Go to the ')}
27+
<a onClick={() => this.context.setPanel('Graph', 'Create')}>
28+
{_('Create')}
29+
</a>
30+
{_(' panel to define traces.')}
31+
</p>
32+
</PanelEmpty>
3033
);
31-
32-
if (this.props.visible) {
33-
conditions.forEach((condition, index) => {
34-
if (!showPanel) {
35-
return;
36-
}
37-
if (!condition()) {
38-
showPanel = false;
39-
emptyPanelMessage.heading = messages[index].heading;
40-
emptyPanelMessage.message = messages[index].message;
41-
}
42-
});
43-
44-
if (showPanel) {
45-
return <LayoutPanel {...rest}>{children}</LayoutPanel>;
46-
}
47-
48-
return (
49-
<PanelEmpty
50-
heading={emptyPanelMessage.heading}
51-
message={emptyPanelMessage.message}
52-
/>
53-
);
54-
}
55-
return null;
5634
}
5735
}
5836

5937
TraceRequiredPanel.propTypes = {
6038
children: PropTypes.node,
6139
visible: PropTypes.bool,
62-
extraConditions: PropTypes.array,
63-
extraEmptyPanelMessages: PropTypes.array,
6440
};
6541

6642
TraceRequiredPanel.defaultProps = {
@@ -70,6 +46,7 @@ TraceRequiredPanel.defaultProps = {
7046
TraceRequiredPanel.contextTypes = {
7147
fullData: PropTypes.array,
7248
localize: PropTypes.func,
49+
setPanel: PropTypes.func,
7350
};
7451

7552
export default TraceRequiredPanel;

src/components/containers/TransformAccordion.js

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import React, {Component} from 'react';
55
import {connectTransformToTrace} from 'lib';
66
import FoldEmpty from './FoldEmpty';
77
import {PlotScatterIcon} from 'plotly-icons';
8+
import {TRANSFORMABLE_TRACES} from 'lib/constants';
89

910
const TransformFold = connectTransformToTrace(PlotlyFold);
1011

@@ -26,17 +27,7 @@ class TransformAccordion extends Component {
2627
{label: _('Sort'), type: 'sort'},
2728
];
2829

29-
const transformableCharts = [
30-
'scatter',
31-
'bar',
32-
'scattergl',
33-
'histogram',
34-
'histogram2d',
35-
'box',
36-
'violin',
37-
];
38-
39-
if (!transformableCharts.includes(fullContainer.type)) {
30+
if (!TRANSFORMABLE_TRACES.includes(fullContainer.type)) {
4031
return (
4132
<FoldEmpty
4233
icon={PlotScatterIcon}

src/components/containers/UpdateMenuAccordion.js

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,7 @@ class UpdateMenuAccordion extends Component {
3838
);
3939
});
4040

41-
return (
42-
<TraceRequiredPanel
43-
extraConditions={[() => updatemenus.length > 0]}
44-
extraEmptyPanelMessages={[
45-
{
46-
heading: _('There are no update menus to style in your plot'),
47-
message: '',
48-
},
49-
]}
50-
>
51-
{content ? content : null}
52-
</TraceRequiredPanel>
53-
);
41+
return <TraceRequiredPanel>{content ? content : null}</TraceRequiredPanel>;
5442
}
5543
}
5644

src/components/fields/AxesCreator.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,11 @@ class UnconnectedAxesCreator extends Component {
165165
<PlotlySection name={_('Axes to Use')}>
166166
{controls}
167167
<Info>
168-
{_(
169-
'You can style and position your axes in the Graph > Subplots Panel'
170-
)}
168+
{_('You can style and position your axes in the ')}
169+
<a onClick={() => this.context.setPanel('Graph', 'Subplots')}>
170+
{_('Subplots')}
171+
</a>
172+
{_(' panel.')}
171173
</Info>
172174
</PlotlySection>
173175
);
@@ -184,6 +186,7 @@ UnconnectedAxesCreator.contextTypes = {
184186
fullData: PropTypes.array,
185187
fullLayout: PropTypes.object,
186188
localize: PropTypes.func,
189+
setPanel: PropTypes.func,
187190
};
188191

189192
export default connectToContainer(UnconnectedAxesCreator, {

src/components/fields/SubplotCreator.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,11 @@ class UnconnectedSubplotCreator extends Component {
145145
options={getOptions(subplotType)}
146146
/>
147147
<Info>
148-
{_(
149-
'You can style and position your subplots in the Graph > Subplots Panel'
150-
)}
148+
{_('You can style and position your subplots in the ')}
149+
<a onClick={() => this.context.setPanel('Graph', 'Subplots')}>
150+
{_('Subplots')}
151+
</a>
152+
{_(' panel.')}
151153
</Info>
152154
</PlotlySection>
153155
);
@@ -164,6 +166,7 @@ UnconnectedSubplotCreator.contextTypes = {
164166
fullData: PropTypes.array,
165167
fullLayout: PropTypes.object,
166168
localize: PropTypes.func,
169+
setPanel: PropTypes.func,
167170
};
168171

169172
export default connectToContainer(UnconnectedSubplotCreator, {

src/default_panels/StyleAxesPanel.js

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,10 @@ import {
2222
} from '../components';
2323

2424
class StyleAxesPanel extends Component {
25-
constructor(props, context) {
26-
super(props, context);
27-
this.hasAxes = this.hasAxes.bind(this);
28-
}
29-
30-
hasAxes() {
31-
return (
32-
Object.keys(this.context.fullLayout._subplots).filter(
33-
type =>
34-
!['cartesian', 'mapbox'].includes(type) &&
35-
this.context.fullLayout._subplots[type].length > 0
36-
).length > 0
37-
);
38-
}
39-
4025
render() {
4126
const {localize: _} = this.context;
4227
return (
43-
<TraceRequiredPanel
44-
extraConditions={[this.hasAxes]}
45-
extraEmptyPanelMessages={[
46-
{
47-
heading: _('Your plot does not have any axes to style.'),
48-
message: '',
49-
},
50-
]}
51-
>
28+
<TraceRequiredPanel>
5229
<AxesFold
5330
name={_('Titles')}
5431
axisFilter={axis =>

0 commit comments

Comments
 (0)