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/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} 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,24 +326,30 @@ 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 ); } 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. plotProps.options = [ - {label: 'in pixels', value: 'pixel'}, - {label: 'according to axis', value: currentAxisRef}, + {label: _('in pixels'), value: 'pixel'}, + {label: _('according to axis'), value: currentAxisRef}, ]; } @@ -357,6 +364,7 @@ export const AnnotationRef = connectToContainer(UnconnectedDropdown, { } const { fullContainer: {axref, ayref}, + localize: _, } = context; let currentOffsetRef; @@ -366,15 +374,23 @@ 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 + + '.' ); } - 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 +418,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; + } }, }); @@ -594,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'}, @@ -602,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; }, 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/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: _}) => ( - - - - - ( + ( + + + + + + + + + + + + + ); diff --git a/src/default_panels/StyleAxesPanel.js b/src/default_panels/StyleAxesPanel.js index 71a65ae4d..7f045f6ee 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,13 @@ 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') ) } > 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..460786f1a 100644 --- a/src/default_panels/StyleTracesPanel.js +++ b/src/default_panels/StyleTracesPanel.js @@ -42,7 +42,7 @@ const StyleTracesPanel = (props, {localize: _}) => ( ( ( 'scatterpolargl', 'scatter3d', 'scattergl', + 'scattergeo', ]} > @@ -350,6 +351,7 @@ const StyleTracesPanel = (props, {localize: _}) => ( 'scatter3d', 'scatterternary', 'bar', + 'scattergeo', ]} > @@ -479,7 +481,7 @@ const StyleTracesPanel = (props, {localize: _}) => ( ( ( ( ]} />