diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js
index 9d12746c8..51cc03457 100644
--- a/src/DefaultEditor.js
+++ b/src/DefaultEditor.js
@@ -17,8 +17,58 @@ import {
StyleUpdateMenusPanel,
} from './default_panels';
import Logo from './components/widgets/Logo';
+import {TRANSFORMABLE_TRACES} from './lib/constants';
class DefaultEditor extends Component {
+ constructor(props, context) {
+ super(props, context);
+ this.hasTransforms = this.hasTransforms.bind(this);
+ this.hasAxes = this.hasAxes.bind(this);
+ this.hasMenus = this.hasMenus.bind(this);
+ this.hasSliders = this.hasSliders.bind(this);
+ this.hasColorbars = this.hasColorbars.bind(this);
+ }
+
+ hasTransforms() {
+ return this.context.fullData.some(d =>
+ TRANSFORMABLE_TRACES.includes(d.type)
+ );
+ }
+
+ hasAxes() {
+ return (
+ Object.keys(this.context.fullLayout._subplots).filter(
+ type =>
+ !['cartesian', 'mapbox'].includes(type) &&
+ this.context.fullLayout._subplots[type].length > 0
+ ).length > 0
+ );
+ }
+
+ hasMenus() {
+ const {
+ fullLayout: {updatemenus = []},
+ } = this.context;
+
+ return updatemenus.length > 0;
+ }
+
+ hasSliders() {
+ const {
+ layout: {sliders = []},
+ } = this.context;
+
+ return sliders.length > 0;
+ }
+
+ hasColorbars() {
+ return this.context.fullData.some(
+ d =>
+ (d.marker && d.marker.showscale !== undefined) || // eslint-disable-line no-undefined
+ d.showscale !== undefined // eslint-disable-line no-undefined
+ );
+ }
+
render() {
const _ = this.context.localize;
const logo = this.props.logoSrc && ;
@@ -28,17 +78,27 @@ class DefaultEditor extends Component {
{logo ? logo : null}
-
+ {this.hasTransforms() && (
+
+ )}
-
+ {this.hasAxes() && (
+
+ )}
-
+ {this.hasColorbars() && (
+
+ )}
-
-
+ {this.hasSliders() && (
+
+ )}
+ {this.hasMenus() && (
+
+ )}
{this.props.children ? this.props.children : null}
);
@@ -52,6 +112,9 @@ DefaultEditor.propTypes = {
DefaultEditor.contextTypes = {
localize: PropTypes.func,
+ fullData: PropTypes.array,
+ fullLayout: PropTypes.object,
+ layout: PropTypes.object,
};
export default DefaultEditor;
diff --git a/src/components/PanelMenuWrapper.js b/src/components/PanelMenuWrapper.js
index 6e98c69cc..b0d79590b 100644
--- a/src/components/PanelMenuWrapper.js
+++ b/src/components/PanelMenuWrapper.js
@@ -23,6 +23,12 @@ class PanelsWithSidebar extends Component {
this.setState({group, panel});
}
+ getChildContext() {
+ return {
+ setPanel: this.setPanel,
+ };
+ }
+
renderSection(section, i) {
if (
section.type &&
@@ -100,4 +106,8 @@ PanelsWithSidebar.propTypes = {
children: PropTypes.node,
};
+PanelsWithSidebar.childContextTypes = {
+ setPanel: PropTypes.func,
+};
+
export default PanelsWithSidebar;
diff --git a/src/components/containers/PanelEmpty.js b/src/components/containers/PanelEmpty.js
index 1a8d2818c..ae30c3870 100644
--- a/src/components/containers/PanelEmpty.js
+++ b/src/components/containers/PanelEmpty.js
@@ -7,7 +7,6 @@ class PanelEmpty extends Component {
render() {
const {children, icon: Icon} = this.props;
const heading = this.props.heading || '';
- const message = this.props.message || '';
return (
@@ -16,10 +15,7 @@ class PanelEmpty extends Component {
{Icon ? : }
{heading}
-
-
{message}
- {children}
-
+ {children}
);
@@ -28,7 +24,6 @@ class PanelEmpty extends Component {
PanelEmpty.propTypes = {
heading: PropTypes.string,
- message: PropTypes.any,
children: PropTypes.node,
icon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
};
diff --git a/src/components/containers/SliderAccordion.js b/src/components/containers/SliderAccordion.js
index 99df6aff5..ef87cbb1f 100644
--- a/src/components/containers/SliderAccordion.js
+++ b/src/components/containers/SliderAccordion.js
@@ -22,19 +22,7 @@ class SliderAccordion extends Component {
));
- return (
- sliders.length > 0]}
- extraEmptyPanelMessages={[
- {
- heading: _('There are no sliders to style in your plot'),
- message: '',
- },
- ]}
- >
- {content ? content : null}
-
- );
+ return {content ? content : null};
}
}
diff --git a/src/components/containers/TraceRequiredPanel.js b/src/components/containers/TraceRequiredPanel.js
index ffd6d1ffe..5d95583f7 100644
--- a/src/components/containers/TraceRequiredPanel.js
+++ b/src/components/containers/TraceRequiredPanel.js
@@ -11,56 +11,32 @@ class TraceRequiredPanel extends Component {
render() {
const {localize: _} = this.context;
const {children, ...rest} = this.props;
- let showPanel = true;
- const emptyPanelMessage = {heading: '', message: ''};
- const noTraceMessage = {
- heading: _("Looks like there aren't any traces defined yet."),
- message: _("Go to the 'Create' tab to define traces."),
- };
-
- const conditions = [() => this.hasTrace()].concat(
- this.props.extraConditions ? this.props.extraConditions : []
- );
+ if (!this.props.visible) {
+ return null;
+ }
- const messages = [noTraceMessage].concat(
- this.props.extraEmptyPanelMessages
- ? this.props.extraEmptyPanelMessages
- : []
+ return this.hasTrace() ? (
+ {children}
+ ) : (
+
+
+ {_('Go to the ')}
+ this.context.setPanel('Graph', 'Create')}>
+ {_('Create')}
+
+ {_(' panel to define traces.')}
+
+
);
-
- if (this.props.visible) {
- conditions.forEach((condition, index) => {
- if (!showPanel) {
- return;
- }
- if (!condition()) {
- showPanel = false;
- emptyPanelMessage.heading = messages[index].heading;
- emptyPanelMessage.message = messages[index].message;
- }
- });
-
- if (showPanel) {
- return {children};
- }
-
- return (
-
- );
- }
- return null;
}
}
TraceRequiredPanel.propTypes = {
children: PropTypes.node,
visible: PropTypes.bool,
- extraConditions: PropTypes.array,
- extraEmptyPanelMessages: PropTypes.array,
};
TraceRequiredPanel.defaultProps = {
@@ -70,6 +46,7 @@ TraceRequiredPanel.defaultProps = {
TraceRequiredPanel.contextTypes = {
fullData: PropTypes.array,
localize: PropTypes.func,
+ setPanel: PropTypes.func,
};
export default TraceRequiredPanel;
diff --git a/src/components/containers/TransformAccordion.js b/src/components/containers/TransformAccordion.js
index 6211bc54e..8f45ae494 100644
--- a/src/components/containers/TransformAccordion.js
+++ b/src/components/containers/TransformAccordion.js
@@ -5,6 +5,7 @@ import React, {Component} from 'react';
import {connectTransformToTrace} from 'lib';
import FoldEmpty from './FoldEmpty';
import {PlotScatterIcon} from 'plotly-icons';
+import {TRANSFORMABLE_TRACES} from 'lib/constants';
const TransformFold = connectTransformToTrace(PlotlyFold);
@@ -26,17 +27,7 @@ class TransformAccordion extends Component {
{label: _('Sort'), type: 'sort'},
];
- const transformableCharts = [
- 'scatter',
- 'bar',
- 'scattergl',
- 'histogram',
- 'histogram2d',
- 'box',
- 'violin',
- ];
-
- if (!transformableCharts.includes(fullContainer.type)) {
+ if (!TRANSFORMABLE_TRACES.includes(fullContainer.type)) {
return (
updatemenus.length > 0]}
- extraEmptyPanelMessages={[
- {
- heading: _('There are no update menus to style in your plot'),
- message: '',
- },
- ]}
- >
- {content ? content : null}
-
- );
+ return {content ? content : null};
}
}
diff --git a/src/components/fields/AxesCreator.js b/src/components/fields/AxesCreator.js
index 3c6f9047b..76b0eb37a 100644
--- a/src/components/fields/AxesCreator.js
+++ b/src/components/fields/AxesCreator.js
@@ -165,9 +165,11 @@ class UnconnectedAxesCreator extends Component {
{controls}
- {_(
- 'You can style and position your axes in the Graph > Subplots Panel'
- )}
+ {_('You can style and position your axes in the ')}
+ this.context.setPanel('Graph', 'Subplots')}>
+ {_('Subplots')}
+
+ {_(' panel.')}
);
@@ -184,6 +186,7 @@ UnconnectedAxesCreator.contextTypes = {
fullData: PropTypes.array,
fullLayout: PropTypes.object,
localize: PropTypes.func,
+ setPanel: PropTypes.func,
};
export default connectToContainer(UnconnectedAxesCreator, {
diff --git a/src/components/fields/SubplotCreator.js b/src/components/fields/SubplotCreator.js
index 156503c0f..c016d4bbb 100644
--- a/src/components/fields/SubplotCreator.js
+++ b/src/components/fields/SubplotCreator.js
@@ -145,9 +145,11 @@ class UnconnectedSubplotCreator extends Component {
options={getOptions(subplotType)}
/>
- {_(
- 'You can style and position your subplots in the Graph > Subplots Panel'
- )}
+ {_('You can style and position your subplots in the ')}
+ this.context.setPanel('Graph', 'Subplots')}>
+ {_('Subplots')}
+
+ {_(' panel.')}
);
@@ -164,6 +166,7 @@ UnconnectedSubplotCreator.contextTypes = {
fullData: PropTypes.array,
fullLayout: PropTypes.object,
localize: PropTypes.func,
+ setPanel: PropTypes.func,
};
export default connectToContainer(UnconnectedSubplotCreator, {
diff --git a/src/default_panels/StyleAxesPanel.js b/src/default_panels/StyleAxesPanel.js
index b34b004ba..7ea641b4d 100644
--- a/src/default_panels/StyleAxesPanel.js
+++ b/src/default_panels/StyleAxesPanel.js
@@ -22,33 +22,10 @@ import {
} from '../components';
class StyleAxesPanel extends Component {
- constructor(props, context) {
- super(props, context);
- this.hasAxes = this.hasAxes.bind(this);
- }
-
- hasAxes() {
- return (
- Object.keys(this.context.fullLayout._subplots).filter(
- type =>
- !['cartesian', 'mapbox'].includes(type) &&
- this.context.fullLayout._subplots[type].length > 0
- ).length > 0
- );
- }
-
render() {
const {localize: _} = this.context;
return (
-
+
diff --git a/src/default_panels/StyleColorbarsPanel.js b/src/default_panels/StyleColorbarsPanel.js
index 13ca8230c..89a7b8690 100644
--- a/src/default_panels/StyleColorbarsPanel.js
+++ b/src/default_panels/StyleColorbarsPanel.js
@@ -18,7 +18,9 @@ import {
const StyleColorBarsPanel = (props, {localize: _}) => {
return (
-
+
{['', 'marker.'].map(prefix => {
return (
polar: _('Polar'),
}[type]);
-export const TRANSFORMS_LIST = ['filter', 'groupby', 'aggregate'];
+export const TRANSFORMS_LIST = ['filter', 'groupby', 'aggregate', 'sort'];
+
+export const TRANSFORMABLE_TRACES = [
+ 'scatter',
+ 'bar',
+ 'scattergl',
+ 'histogram',
+ 'histogram2d',
+ 'box',
+ 'violin',
+];
export const COLORS = {
charcoal: '#444444',
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 5f57a1ba3..e1479c036 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -31,6 +31,10 @@
display: flex;
flex-grow: 1;
}
+ a {
+ color: $color-dodger-shade;
+ cursor: pointer;
+ }
}
.plotly_editor {