From 2dabcf3d83d323cb90b4c74c677aa33a209195fe Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 8 Aug 2018 15:59:26 -0400 Subject: [PATCH 1/8] add geo layout to subplot panel --- src/default_panels/GraphSubplotsPanel.js | 114 +++++++++++++---------- 1 file changed, 65 insertions(+), 49 deletions(-) diff --git a/src/default_panels/GraphSubplotsPanel.js b/src/default_panels/GraphSubplotsPanel.js index 3aa101da5..b90997482 100644 --- a/src/default_panels/GraphSubplotsPanel.js +++ b/src/default_panels/GraphSubplotsPanel.js @@ -67,55 +67,6 @@ const GraphSubplotsPanel = (props, {localize: _}) => ( - - - - - ( + ( + + + + + + + + + + + + + ); From d201d734fc4a1cef6c968bac0172db3bf0724601 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 8 Aug 2018 18:11:58 -0400 Subject: [PATCH 2/8] fix: could not create notes/shapes/images on many non-cartesian traces --- src/components/fields/derived.js | 41 ++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js index 9fda4242c..225e4ce40 100644 --- a/src/components/fields/derived.js +++ b/src/components/fields/derived.js @@ -333,10 +333,15 @@ export const AnnotationArrowRef = connectToContainer(UnconnectedDropdown, { if (currentAxisRef === 'paper') { // If currentAxesRef is paper provide all axes options to user. - plotProps.options = [ - {label: 'in pixels', value: 'pixel'}, - ...computeAxesRefOptions(getAllAxes(context.fullLayout), props.attr), - ]; + const axes = getAllAxes(context.fullLayout).filter(a => a._id); + if (axes.length > 0) { + plotProps.options = [ + {label: 'in pixels', value: 'pixel'}, + ...computeAxesRefOptions(axes, props.attr), + ]; + } else { + plotProps.isVisible = false; + } } else { // If currentAxesRef is an actual axes then offer that value as the only // axes option. @@ -371,10 +376,15 @@ export const AnnotationRef = connectToContainer(UnconnectedDropdown, { ); } - plotProps.options = [ - {label: 'Canvas', value: 'paper'}, - ...computeAxesRefOptions(getAllAxes(context.fullLayout), props.attr), - ]; + const axes = getAllAxes(context.fullLayout).filter(a => a._id); + if (axes.length > 0) { + plotProps.options = [ + {label: 'Canvas', value: 'paper'}, + ...computeAxesRefOptions(axes, props.attr), + ]; + } else { + plotProps.isVisible = false; + } if (currentOffsetRef !== 'pixel') { plotProps.updatePlot = v => { @@ -402,12 +412,17 @@ export const AnnotationRef = connectToContainer(UnconnectedDropdown, { export const PositioningRef = connectToContainer(UnconnectedDropdown, { modifyPlotProps: (props, context, plotProps) => { - plotProps.options = [ - {label: 'Canvas', value: 'paper'}, - ...computeAxesRefOptions(getAllAxes(context.fullLayout), props.attr), - ]; + const axes = getAllAxes(context.fullLayout).filter(a => a._id); + if (axes.length > 0) { + plotProps.options = [ + {label: 'Canvas', value: 'paper'}, + ...computeAxesRefOptions(axes, props.attr), + ]; - plotProps.clearable = false; + plotProps.clearable = false; + } else { + plotProps.isVisible = false; + } }, }); From 7d755f30c21ecb36229765723c414776f912ad3f Mon Sep 17 00:00:00 2001 From: dmt0 Date: Wed, 8 Aug 2018 20:14:35 -0400 Subject: [PATCH 3/8] add localization to ALL labels --- src/EditorControls.js | 10 +- src/components/containers/ModalProvider.js | 10 +- src/components/fields/AxesCreator.js | 2 +- src/components/fields/AxesSelector.js | 3 +- src/components/fields/FilterOperation.js | 112 +++++++++++--------- src/components/fields/derived.js | 22 ++-- src/components/widgets/TraceTypeSelector.js | 54 ++++++---- src/default_panels/StyleLayoutPanel.js | 4 +- src/default_panels/StyleNotesPanel.js | 4 +- src/default_panels/StyleTracesPanel.js | 12 +-- 10 files changed, 133 insertions(+), 100 deletions(-) diff --git a/src/EditorControls.js b/src/EditorControls.js index e21ae8f33..071c60f29 100644 --- a/src/EditorControls.js +++ b/src/EditorControls.js @@ -20,6 +20,9 @@ class EditorControls extends Component { constructor(props, context) { super(props, context); + this.localize = key => + localizeString(this.props.dictionaries || {}, this.props.locale, key); + // we only need to compute this once. if (this.props.plotly) { this.plotSchema = this.props.plotly.PlotSchema.get(); @@ -38,8 +41,7 @@ class EditorControls extends Component { dataSourceValueRenderer: this.props.dataSourceValueRenderer, dataSourceOptionRenderer: this.props.dataSourceOptionRenderer, dictionaries: this.props.dictionaries || {}, - localize: key => - localizeString(this.props.dictionaries || {}, this.props.locale, key), + localize: this.localize, frames: gd._transitionData ? gd._transitionData._frames : [], fullData: gd._fullData, fullLayout: gd._fullLayout, @@ -281,7 +283,9 @@ class EditorControls extends Component { break; default: - throw new Error('must specify an action type to handleEditorUpdate'); + throw new Error( + this.localize('must specify an action type to handleEditorUpdate') + ); } } diff --git a/src/components/containers/ModalProvider.js b/src/components/containers/ModalProvider.js index 919e3bdbd..bc46ccd0f 100644 --- a/src/components/containers/ModalProvider.js +++ b/src/components/containers/ModalProvider.js @@ -27,16 +27,17 @@ class ModalProvider extends React.Component { } } - openModal(component, props) { + openModal(component, componentProps) { + const {localize: _} = this.context; if (!component) { - throw Error('You need to provide a component for the modal to open!'); + throw Error(_('You need to provide a component for the modal to open!')); } const {open} = this.state; if (!open) { this.setState({ component: component, - componentProps: props, + componentProps: componentProps, open: true, }); } @@ -85,6 +86,9 @@ class ModalProvider extends React.Component { ModalProvider.propTypes = { children: PropTypes.node, }; +ModalProvider.contextTypes = { + localize: PropTypes.func, +}; ModalProvider.childContextTypes = { openModal: PropTypes.func, closeModal: PropTypes.func, diff --git a/src/components/fields/AxesCreator.js b/src/components/fields/AxesCreator.js index cb4e294bc..3c6f9047b 100644 --- a/src/components/fields/AxesCreator.js +++ b/src/components/fields/AxesCreator.js @@ -154,7 +154,7 @@ class UnconnectedAxesCreator extends Component { ); diff --git a/src/components/fields/AxesSelector.js b/src/components/fields/AxesSelector.js index fa3134d99..8f208c634 100644 --- a/src/components/fields/AxesSelector.js +++ b/src/components/fields/AxesSelector.js @@ -7,10 +7,11 @@ import React, {Component} from 'react'; class AxesSelector extends Component { constructor(props, context) { super(props, context); + const {localize: _} = context; if (!context.axesTargetHandler) { throw new Error( - 'AxesSelector must be nested within a connectAxesToPlot component' + _('AxesSelector must be nested within a connectAxesToPlot component') ); } } diff --git a/src/components/fields/FilterOperation.js b/src/components/fields/FilterOperation.js index 5f1fa80d9..8389a7a91 100644 --- a/src/components/fields/FilterOperation.js +++ b/src/components/fields/FilterOperation.js @@ -5,60 +5,38 @@ import DropdownWidget from '../widgets/Dropdown'; import TextInput from '../widgets/TextInput'; import {connectToContainer} from 'lib'; -const operators = [ - { - label: 'Inequality', - value: 'inequality', - }, - { - label: 'Include Range', - value: 'inrange', - }, - { - label: 'Exclude Range', - value: 'exrange', - }, - { - label: 'Include Values', - value: 'inset', - }, - { - label: 'Exclude Values', - value: 'exset', - }, -]; - -const operations = { +const operations = _ => ({ inequality: [ - {value: '!=', label: 'Target ≠ Reference'}, - {value: '<', label: 'Target < Reference'}, - {value: '<=', label: 'Target ≤ Reference'}, - {value: '=', label: 'Target = Reference'}, - {value: '>', label: 'Target > Reference'}, - {value: '>=', label: 'Target ≥ Reference'}, + {value: '!=', label: _('Target ≠ Reference')}, + {value: '<', label: _('Target < Reference')}, + {value: '<=', label: _('Target ≤ Reference')}, + {value: '=', label: _('Target = Reference')}, + {value: '>', label: _('Target > Reference')}, + {value: '>=', label: _('Target ≥ Reference')}, ], inrange: [ - {value: '[]', label: 'Lower ≤ Target ≤ Upper'}, - {value: '()', label: 'Lower < Target < Upper'}, - {value: '[)', label: 'Lower ≤ Target < Upper'}, - {value: '(]', label: 'Lower < Target ≤ Upper'}, + {value: '[]', label: _('Lower ≤ Target ≤ Upper')}, + {value: '()', label: _('Lower < Target < Upper')}, + {value: '[)', label: _('Lower ≤ Target < Upper')}, + {value: '(]', label: _('Lower < Target ≤ Upper')}, ], exrange: [ - {value: ')(', label: 'Lower ≤ Target ≤ Upper'}, - {value: '][', label: 'Lower < Target < Upper'}, - {value: ')[', label: 'Lower ≤ Target < Upper'}, - {value: '](', label: 'Lower < Target ≤ Upper'}, + {value: ')(', label: _('Lower ≤ Target ≤ Upper')}, + {value: '][', label: _('Lower < Target < Upper')}, + {value: ')[', label: _('Lower ≤ Target < Upper')}, + {value: '](', label: _('Lower < Target ≤ Upper')}, ], - inset: [{value: '{}', label: 'Include'}], - exset: [{value: '}{', label: 'Exclude'}], -}; + inset: [{value: '{}', label: _('Include')}], + exset: [{value: '}{', label: _('Exclude')}], +}); -const findOperation = operator => { +const findOperation = (operator, _) => { let op = 'inequality'; - for (const key in operations) { + const ops = operations(_); + for (const key in ops) { if ( - operations.hasOwnProperty(key) && - operations[key].map(o => o.value).indexOf(operator) !== -1 + ops.hasOwnProperty(key) && + ops[key].map(o => o.value).indexOf(operator) !== -1 ) { op = key; break; @@ -70,17 +48,19 @@ const findOperation = operator => { class UnconnectedFilterOperation extends Component { constructor(props, context) { super(props, context); + const {localize: _} = context; this.state = { - operation: findOperation(this.props.fullValue), - operator: operations.inequality[0].value, + operation: findOperation(this.props.fullValue, _), + operator: operations(_).inequality[0].value, }; this.setOperation = this.setOperation.bind(this); } setOperation(value) { - const operator = operations[value][0].value; + const {localize: _} = this.context; + const operator = operations(_)[value][0].value; this.setState({operation: value, operator: operator}); this.props.updatePlot(operator); } @@ -94,6 +74,30 @@ class UnconnectedFilterOperation extends Component { backgroundDark, attr, } = this.props; + const {localize: _} = this.context; + + const operators = [ + { + label: _('Inequality'), + value: 'inequality', + }, + { + label: _('Include Range'), + value: 'inrange', + }, + { + label: _('Exclude Range'), + value: 'exrange', + }, + { + label: _('Include Values'), + value: 'inset', + }, + { + label: _('Exclude Values'), + value: 'exset', + }, + ]; const opValue = fullValue && fullValue.length > 0 ? fullValue : this.state.operator; @@ -103,7 +107,7 @@ class UnconnectedFilterOperation extends Component { { + const {localize: _} = context; if (!context.fullContainer) { return; } @@ -325,8 +326,9 @@ export const AnnotationArrowRef = connectToContainer(UnconnectedDropdown, { currentAxisRef = yref; } else { throw new Error( - 'AnnotationArrowRef must be given either "axref" or "ayref" as attrs. ' + - `Instead was given "${props.attr}".` + _( + 'AnnotationArrowRef must be given either "axref" or "ayref" as attrs. Instead was given' + ) + props.attr ); } @@ -336,7 +338,7 @@ export const AnnotationArrowRef = connectToContainer(UnconnectedDropdown, { const axes = getAllAxes(context.fullLayout).filter(a => a._id); if (axes.length > 0) { plotProps.options = [ - {label: 'in pixels', value: 'pixel'}, + {label: _('in pixels'), value: 'pixel'}, ...computeAxesRefOptions(axes, props.attr), ]; } else { @@ -346,8 +348,8 @@ export const AnnotationArrowRef = connectToContainer(UnconnectedDropdown, { // If currentAxesRef is an actual axes then offer that value as the only // axes option. plotProps.options = [ - {label: 'in pixels', value: 'pixel'}, - {label: 'according to axis', value: currentAxisRef}, + {label: _('in pixels'), value: 'pixel'}, + {label: _('according to axis'), value: currentAxisRef}, ]; } @@ -362,6 +364,7 @@ export const AnnotationRef = connectToContainer(UnconnectedDropdown, { } const { fullContainer: {axref, ayref}, + localize: _, } = context; let currentOffsetRef; @@ -371,15 +374,18 @@ export const AnnotationRef = connectToContainer(UnconnectedDropdown, { currentOffsetRef = ayref; } else { throw new Error( - 'AnnotationRef must be given either "xref" or "yref" as attrs. ' + - `Instead was given "${props.attr}".` + _( + 'AnnotationRef must be given either "xref" or "yref" as attrs. Instead was given' + ) + + props.attr + + '.' ); } const axes = getAllAxes(context.fullLayout).filter(a => a._id); if (axes.length > 0) { plotProps.options = [ - {label: 'Canvas', value: 'paper'}, + {label: _('Canvas'), value: 'paper'}, ...computeAxesRefOptions(axes, props.attr), ]; } else { diff --git a/src/components/widgets/TraceTypeSelector.js b/src/components/widgets/TraceTypeSelector.js index 244947406..802ffe743 100644 --- a/src/components/widgets/TraceTypeSelector.js +++ b/src/components/widgets/TraceTypeSelector.js @@ -9,24 +9,6 @@ import { plotlyTraceToCustomTrace, } from 'lib'; -const actions = ({value}) => [ - { - label: `Charts like this by Plotly users.`, - href: `https://plot.ly/feed/?q=plottype:${value}`, - icon: , - }, - { - label: `View tutorials on this chart type.`, - href: `#`, - icon: , - }, - { - label: `See a basic example.`, - href: `#`, - icon: , - }, -]; - const renderActionItems = (actionItems, item) => actionItems ? actionItems(item).map((action, i) => ( @@ -44,7 +26,11 @@ const renderActionItems = (actionItems, item) => )) : null; -const Item = ({item, active, handleClick, actions, showActions, complex}) => { +const Item = ( + {item, active, handleClick, actions, showActions, complex}, + context +) => { + const {localize: _} = context; const {label, value, icon} = item; const SimpleIcon = renderTraceIcon(icon ? icon : value); const ComplexIcon = renderTraceIcon(icon ? icon : value, 'TraceType'); @@ -69,7 +55,7 @@ const Item = ({item, active, handleClick, actions, showActions, complex}) => { )} -
{label}
+
{_(label)}
); }; @@ -93,6 +79,27 @@ class TraceTypeSelector extends Component { this.context.handleClose(); } + actions({value}) { + const {localize: _} = this.context; + return [ + { + label: _('Charts like this by Plotly users.'), + href: `https://plot.ly/feed/?q=plottype:${value}`, + icon: , + }, + { + label: _('View tutorials on this chart type.'), + href: '#', + icon: , + }, + { + label: _('See a basic example.'), + href: '#', + icon: , + }, + ]; + } + renderCategories() { const {fullValue} = this.props; const { @@ -128,7 +135,7 @@ class TraceTypeSelector extends Component { key={item.value} active={fullValue === item.value} item={item} - actions={actions} + actions={this.actions} showActions={false} handleClick={() => this.selectAndClose(item.value)} /> @@ -156,7 +163,7 @@ class TraceTypeSelector extends Component { complex={complex} active={fullValue === item.value} item={item} - actions={actions} + actions={this.actions} showActions={false} handleClick={() => this.selectAndClose(item.value)} style={{display: 'inline-block'}} @@ -250,5 +257,8 @@ Item.propTypes = { actions: PropTypes.func, showActions: PropTypes.bool, }; +Item.contextTypes = { + localize: PropTypes.func, +}; export default TraceTypeSelector; diff --git a/src/default_panels/StyleLayoutPanel.js b/src/default_panels/StyleLayoutPanel.js index 3e54bfbf7..1c6c763c1 100644 --- a/src/default_panels/StyleLayoutPanel.js +++ b/src/default_panels/StyleLayoutPanel.js @@ -32,7 +32,7 @@ const StyleLayoutPanel = (props, {localize: _}) => ( ( clearable={false} /> ( - - + + diff --git a/src/default_panels/StyleTracesPanel.js b/src/default_panels/StyleTracesPanel.js index f91fc5497..15313c164 100644 --- a/src/default_panels/StyleTracesPanel.js +++ b/src/default_panels/StyleTracesPanel.js @@ -42,7 +42,7 @@ const StyleTracesPanel = (props, {localize: _}) => ( ( ( ( ( ( ]} /> Date: Thu, 9 Aug 2018 12:28:04 -0400 Subject: [PATCH 4/8] axes panel for geo - visibility issues and missing widgets --- src/default_panels/StyleAxesPanel.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/default_panels/StyleAxesPanel.js b/src/default_panels/StyleAxesPanel.js index 71a65ae4d..786a64c15 100644 --- a/src/default_panels/StyleAxesPanel.js +++ b/src/default_panels/StyleAxesPanel.js @@ -49,7 +49,9 @@ class StyleAxesPanel extends Component { > !axis._name.includes('angular')} + axisFilter={axis => + !(axis._name.includes('angular') || axis._subplot.includes('geo')) + } > @@ -150,6 +152,8 @@ class StyleAxesPanel extends Component { /> + + - + !axis._subplot.includes('geo')} + > - + !axis._subplot.includes('geo')} + > !( axis._subplot.includes('ternary') || - axis._subplot.includes('polar') + axis._subplot.includes('polar') || + axis._subplot.includes('geo') ) } > From 1d9842453362448474e1ce48bb50523313cb1629 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 9 Aug 2018 12:39:02 -0400 Subject: [PATCH 5/8] correct options for fill dropdown --- src/components/fields/derived.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/components/fields/derived.js b/src/components/fields/derived.js index 95634b820..bff1a1217 100644 --- a/src/components/fields/derived.js +++ b/src/components/fields/derived.js @@ -615,7 +615,11 @@ export const FillDropdown = connectToContainer(UnconnectedDropdown, { {label: _('Previous X'), value: 'tonextx'}, ]; - if (context.container.type === 'scatterternary') { + if ( + context.container.type === 'scatterternary' || + context.container.type === 'scattercarpet' || + context.container.type === 'scatterpolar' + ) { options = [ {label: _('None'), value: 'none'}, {label: _('To Self'), value: 'toself'}, @@ -623,6 +627,16 @@ export const FillDropdown = connectToContainer(UnconnectedDropdown, { ]; } + if ( + context.container.type === 'scattergeo' || + context.container.type === 'scattermapbox' + ) { + options = [ + {label: _('None'), value: 'none'}, + {label: _('To Self'), value: 'toself'}, + ]; + } + plotProps.options = options; plotProps.clearable = false; }, From 04db3925648f800bbfaf216eca7c558f0532e93b Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 9 Aug 2018 12:48:56 -0400 Subject: [PATCH 6/8] StyleTracePanel sections missing for atlas maps --- src/default_panels/StyleAxesPanel.js | 9 +++++++-- src/default_panels/StyleTracesPanel.js | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/default_panels/StyleAxesPanel.js b/src/default_panels/StyleAxesPanel.js index 786a64c15..7f045f6ee 100644 --- a/src/default_panels/StyleAxesPanel.js +++ b/src/default_panels/StyleAxesPanel.js @@ -152,8 +152,13 @@ class StyleAxesPanel extends Component { /> - - + + + + ( 'scatterpolargl', 'scatter3d', 'scattergl', + 'scattergeo', ]} > @@ -350,6 +351,7 @@ const StyleTracesPanel = (props, {localize: _}) => ( 'scatter3d', 'scatterternary', 'bar', + 'scattergeo', ]} > From e21307787f98423049d34b0da07ee54c74729414 Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 9 Aug 2018 14:08:26 -0400 Subject: [PATCH 7/8] marker colors: clear showscale when user switches to constant --- src/components/fields/MarkerColor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/fields/MarkerColor.js b/src/components/fields/MarkerColor.js index 30fd0a5b2..b294ffb92 100644 --- a/src/components/fields/MarkerColor.js +++ b/src/components/fields/MarkerColor.js @@ -57,6 +57,7 @@ class UnconnectedMarkerColor extends Component { this.context.updateContainer({ ['marker.colorsrc']: null, ['marker.colorscale']: null, + ['marker.showscale']: null, }); this.setState({colorscale: null}); } else { From f2fe6dee18d46e2904831e6063c3500857b01c0d Mon Sep 17 00:00:00 2001 From: dmt0 Date: Thu, 9 Aug 2018 14:34:50 -0400 Subject: [PATCH 8/8] proper trace names in TraceAccordion --- src/components/containers/TraceAccordion.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/containers/TraceAccordion.js b/src/components/containers/TraceAccordion.js index faad5d797..91a280166 100644 --- a/src/components/containers/TraceAccordion.js +++ b/src/components/containers/TraceAccordion.js @@ -6,6 +6,7 @@ import React, {Component} from 'react'; import {EDITOR_ACTIONS} from 'lib/constants'; import {connectTraceToPlot, plotlyTraceToCustomTrace} from 'lib'; import {Tab, Tabs, TabList, TabPanel} from 'react-tabs'; +import {traceTypes} from 'lib/traceTypes'; const TraceFold = connectTraceToPlot(PlotlyFold); @@ -38,6 +39,7 @@ class TraceAccordion extends Component { return null; } + const {localize: _} = this.context; const dataArrayPositionsByTraceType = {}; const fullDataArrayPositionsByTraceType = {}; @@ -62,7 +64,7 @@ class TraceAccordion extends Component { t.value === type).label} fullDataArrayPosition={fullDataArrayPositionsByTraceType[type]} > {this.props.children}